为何分配给基本呼叫的结果,为什么不是编译器错误

Why is it not a compiler error to assign to the result of a substr call?

本文关键字:结果 为什么不 错误 编译器 呼叫 何分配 分配      更新时间:2023-10-16

我刚刚发现了最令人困惑的错误,我不明白为什么编译器没有为我标记它。如果我写以下内容:

string s = "abcdefghijkl";
cout << s << endl;
s.substr(2,3) = "foo";
s.substr(8,1) = '.';
s.substr(9,1) = 4;
cout << s << endl;

编译器对此没有任何问题,并且根据打印的内容,分配语句似乎没有效果。相反,

s.front() = 'x';

具有更改基础字符串的效果(因为front返回对字符的引用),

s.length() = 4;

还具有产生编译器错误的预期效果,因为length返回整数,因此无法将您分配给不是LVALUE的东西。(无论如何,size_t。)

那么...为什么编译器在地球上不抱怨分配给substr呼叫的结果?它返回一个字符串值,而不是引用,因此不应该分配,对吗?但是我已经在g++(6.2.1)和clang++(3.9.0)中尝试过,因此它似乎不是一个错误,并且似乎对C 版本也不敏感(尝试03,11 11,14)。

substr()的结果是std::string临时对象 - 它是子字符串的独立副本,而不是原始字符串的视图。

作为std::string对象,它具有分配运算符函数,并且您的代码调用该功能来修改临时对象。

这有点令人惊讶 - 修改临时对象并丢弃结果通常表明逻辑错误,因此通常有两种方法可以尝试改善情况:

  1. 返回const对象。
  2. 在分配运算符上使用lvalue ref-Qualifier。

选项1将导致代码的汇编错误,但它还限制了某些有效的用例(例如, move -ing退出返回值 - 您无法移出const字符串)。

选项2除非左侧是LVALUE,否则可以防止使用分配运算符。恕我直言,这是一个好主意,尽管并非所有人都同意。请参阅此线程以获取讨论。

无论如何;当在C 11中添加参考Qualifiers时,建议您返回并更改C 03的所有容器的规范,但是该提案不接受(大概,如果它打破了现有代码,则该提案)。p> std::string是在1990年代设计的,并做出了一些设计选择,这些选择在今天,事后看来似乎很差,但是我们坚持了下来。您只需要了解std::string本身的问题,也许可以通过使用参考Qualifiers,视图或其他方式在您自己的课程中避免它。

您的代码编译是因为它是合法的C 。这是一个解释发生了什么的链接。

https://accu.org/index.php/journals/227

这很长,所以我会引用最相关的部分:

非级别的rvalues不可修改,也不能具有CV合格类型 (忽略了CV的物质)。相反,班级 rvalues是可修改的,可用于通过其对象修改对象 成员功能。他们也可以具有CV合成类型。

因此,您无法分配给std::string::length返回的RVALUE的原因是因为它不是类的实例,并且可以分配给从std::string::substr返回的RVALUE的原因是因为它是类的实例。

我不完全了解为什么这种语言是这样定义的,但这就是这样。

查看代码:

s.substr(2,3) = "foo";

substr的函数调用返回一个字符串,即对象和临时值。之后,您可以修改此对象(实际上是通过调用std::string类的重载分配运算符)。这个临时对象不会以任何方式保存。编译器只是摧毁了这一修改后的临时性。

此代码没有意义。您可能会问,为什么编译器不发出警告?答案是编译器可能会更好。编译器是由人而不是神写的。不幸的是,C 允许编写触发未定义行为的无意义代码或代码的各种方式。这是该语言的方面之一。与许多其他语言相比,它需要更好的知识和更高的关注。

我刚刚检查了MSVC 2015,代码:

std::string s1 = "abcdef";
s1.substr(1, 2) = "5678";

编译罚款。

相关文章: