“”+在C ++中的东西
我一直在我的代码中发生非常怪异的东西。 我相信我已经把它追踪到标有“here”的部分(当然,代码是简化的):
std::string func() { char c; // Do stuff that will assign to c return "" + c; // Here }
当我试图说明这个function的结果时,会发生各种各样的事情。 我想我甚至设法得到一些底层的C ++文档,还有许多分段错误 。 我很清楚,这不适用于C + +(我已经诉诸使用stringstream
转换string
现在),但我想知道为什么。 在使用了很多C#之后,没有C ++,这让我感到很痛苦。
-
""
是一个string文字。 那些有Nconst char
的types数组 。 这个特定的string文字是一个1个const char
的数组 ,一个元素是空终止符。 -
数组很容易衰减成指向它们第一个元素的指针,例如在需要指针的expression式中。
-
lhs + rhs
没有为数组定义为lhs
和整数为rhs
。 但是它是通过指针算术将指针定义为lhs和整数作为rhs。 -
char
是C ++核心语言中的一个整型数据types(即被视为一个整数)。
==> string文字+
字符因此被解释为指针+
整数 。
expression"" + c
大致相当于:
static char const lit[1] = {'\0'}; char const* p = &lit[0]; p + c // "" + c is roughly equivalent to this expression
你返回一个std::string
。 expression式"" + c
产生一个指向const char
的指针 。 需要const char*
的std::string
构造const char*
期望它是一个指向以null结尾的字符数组的指针。
如果c != 0
,则expression式"" + c
导致未定义的行为:
-
对于
c > 1
,指针运算产生未定义的行为。 指针运算只在数组上定义,如果结果是同一个数组的元素。 -
如果
char
被签名,那么出于同样的原因,c < 0
会产生Undefined Behavior。 -
对于
c == 1
,指针运算不会产生未定义的行为。 这是一个特例。 指向数组的最后一个元素之后的一个元素是允许的(虽然不允许使用它指向的元素)。 它仍然会导致未定义行为,因为这里调用的std::string
构造函数要求它的参数是一个指向有效数组的指针(和一个以null结尾的string)。 过去的最后一个元素不是数组本身的一部分。 违反这个要求也导致UB。
现在可能发生的情况是, std::string
的构造函数试图通过search数组中等于'\0'
的(第一个)字符来确定传递给它的以null结尾的string的大小:
string(char const* p) { // simplified char const* end = p; while(*end != '\0') ++end; //... }
这会产生访问冲突,或者它创build的string包含“垃圾”。 编译器假定这种“未定义的行为”永远不会发生,并且会进行一些有趣的优化,这会导致奇怪的行为。
顺便说一句, 铿锵+3.5发出一个很好的警告这个片段:
警告:向string添加'char'不会附加到string[-Wstring-plus-int]
return "" + c; // Here ~~~^~~
注意:使用数组索引来消除这个警告
关于编译器如何解释这段代码有很多解释,但是你可能想知道的是你做错了什么。
你似乎期待从std::string
的+
行为。 问题是这两个操作数实际上都不是一个std::string
。 C ++查看操作数的types,而不是expression式的最终types(这里是返回types, std::string
)来parsing重载。 如果它没有看到一个std::string
,它将不会selectstd::string
的版本。
如果你对某个操作符有特殊的行为(不pipe是你写的,还是有一个提供它的库),那么这个行为只适用于至less有一个操作数具有类types(或对类types的引用,以及用户定义的枚举数太)。
如果你写了
std::string("") + c
要么
std::string() + c
要么
""s + c // requires C++14
那么你将得到operator +的std::string
行为。
(请注意,这些实际上都不是很好的解决scheme,因为它们都使std::string(1, c)
可以避免的短期std::string
实例)
function也一样。 这是一个例子:
std::complex<double> ipi = std::log(-1.0);
你会得到一个运行时错误,而不是预期的虚数。 这是因为编译器不知道它应该在这里使用复数对数。 重载仅看参数,参数是一个实数(实际上是double
types)。
运算符重载ARE函数并遵守相同的规则。
这个返回语句
return "" + c;
已validation。 有所谓的指针算术。 string文字“”被转换为指向其第一个字符的指针(在本例中为其终止零),存储在c中的整数值被添加到指针。 所以expression的结果
"" + c
有typesconst char *
类std :: string有接受types为const char *
参数的转换构造const char *
。 问题是,这个指针可以指向超出string文字。 所以这个函数有不确定的行为。
我没有看到使用这个expression式的任何意义。 如果你想build立一个基于一个字符的string,你可以写例如
return std::string( 1, c );
C ++和C#的区别在于,在C#中,string文字的typesSystem.String已经重载了string和字符(即C#中的Unicode字符)的运算符+。 在C ++中,string是常量字符数组,而数组和整数的运算符+的语义是不同的。 数组被转换为指向其第一个元素的指针,并使用指针算术。
它是标准的类std :: string,它已经为字符重载了operator +。 C ++中的string文字不是types为std :: string的对象。