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