" " + C++的东西

"" + something in C++

本文关键字:C++      更新时间:2023-10-16

我的代码中发生了一些非常奇怪的事情。我相信我已经找到了标记为"这里"的部分(当然,代码是简化的):

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类型的对象。

相关文章:
  • 没有找到相关文章