为何分配给基本呼叫的结果,为什么不是编译器错误
Why is it not a compiler error to assign to the result of a substr call?
我刚刚发现了最令人困惑的错误,我不明白为什么编译器没有为我标记它。如果我写以下内容:
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
对象,它具有分配运算符函数,并且您的代码调用该功能来修改临时对象。
这有点令人惊讶 - 修改临时对象并丢弃结果通常表明逻辑错误,因此通常有两种方法可以尝试改善情况:
- 返回
const
对象。 - 在分配运算符上使用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";
编译罚款。
- 为什么不;名字在地图上是按顺序排列的吗
- 使用取消引用的指针的多态性会产生意外的结果.为什么?
- 为什么不能修改对象中的值?另外,我如何改进此链表?
- 为什么不调用移动构造函数?(默认情况下只有构造器,没有别的)
- C++ 基本 CTOR 说明 - 为什么不调用赋值/复制构造函数
- 为什么不递增?(构造函数)
- 为什么不允许成员函数和非成员函数之间的函数重载?
- 为什么不允许使用可变长度数组作为向量元素?
- C++:为什么不调用移动构造函数?
- 在 C++ 中声明 const 对象需要用户定义的默认构造函数.如果我有一个可变成员变量,为什么不呢?
- 为什么不能用常量表达式声明数组?
- 为什么不能直接引用作用域枚举类成员,而不能为无作用域枚举生成类成员?
- C++ queue.front();为什么不从第一个元素开始呢?
- 为何分配给基本呼叫的结果,为什么不是编译器错误
- 为什么不允许我将函数返回 const char* 的结果分配给 char*,bt 可以将字符串文字(常量)分配给 cha
- 为什么 bool 显示正确的位顺序,而直接输出移位运算结果却不显示?
- 为什么不从临时对象中调用move ctor进行构造(运算符+的结果)
- 为什么不通过std::cin复制并粘贴到控制台中读取会产生与从std::ifstream读取相同的结果呢
- 为什么getpeername()返回OK,结果却不是预期的
- 按位运算的意外结果 为什么 (1 | 1 & 2) 给出 1 而不是 2?