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