Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/129.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么分配给substr调用的结果不是编译器错误?_C++_String_Substring_Lvalue - Fatal编程技术网

C++ 为什么分配给substr调用的结果不是编译器错误?

C++ 为什么分配给substr调用的结果不是编译器错误?,c++,string,substring,lvalue,C++,String,Substring,Lvalue,我刚刚发现了最令人困惑的错误,我不明白为什么编译器没有为我标记它。如果我写以下内容: string s = "abcdefghijkl"; cout << s << endl; s.substr(2,3) = "foo"; s.substr(8,1) = '.'; s.substr(9,1) = 4; cout << s << endl; 具有更改基础字符串的预期效果(因为front返回对字符的引用),并且 s.length() = 4; 还

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

string s = "abcdefghijkl";
cout << s << endl;

s.substr(2,3) = "foo";
s.substr(8,1) = '.';
s.substr(9,1) = 4;
cout << s << endl;
具有更改基础字符串的预期效果(因为
front
返回对字符的引用),并且

s.length() = 4;
还具有生成编译器错误的预期效果,该错误抱怨您无法为非左值的内容赋值,因为
length
返回整数。(好吧,反正是一个
size\u t

所以。。。究竟为什么编译器不抱怨分配给
substr
调用的结果?它返回一个字符串值,而不是引用,所以它不应该是可赋值的,对吗?但是我在代码< > g++< /c>(6.2.1)和 CLang++< /Cuff>(3.9)中尝试过,因此它看起来并不是一个bug,而且它似乎也不太敏感于C++版本(尝试03, 11,14)。
s.substr(2,3) = "foo";
std::string s1 = "abcdef";
s1.substr(1, 2) = "5678";
substr
的函数调用返回一个字符串,该字符串是一个对象和一个临时值。然后修改这个对象(实际上是从
std::string
类调用重载赋值操作符)。此临时对象不会以任何方式保存。编译器只是销毁这个修改过的临时文件

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

我刚刚与MSVC 2015进行了核对,代码:

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

<>编译好.< /p> 代码编译的原因是因为它是合法C++。这里有一个链接可以解释发生了什么

它相当长,所以我将引用最相关的部分:

非类R值不可修改,也不能具有cv限定类型 (简历资格被忽略)。相反,这个班级 R值是可修改的,可用于通过其属性修改对象 成员职能。它们也可以具有cv限定类型

因此,无法分配给
std::string::length
返回的右值的原因是因为它不是类的实例,而可以分配给
std::string::substr
返回的右值的原因是因为它是类的实例

我不太明白为什么语言是这样定义的,但事实就是这样。

substr()的结果是一个
std::string
临时对象——它是子字符串的自包含副本,而不是原始字符串的视图

作为一个
std::string
对象,它有一个赋值操作符函数,您的代码调用该函数来修改临时对象

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

  • 返回一个
    const
    对象
  • 在赋值运算符上使用左值ref限定符
  • 选项1会导致代码的编译错误,但它也会限制一些有效的用例(例如,
    move
    -ing移出返回值——您不能移出
    const
    字符串)

    选项2防止使用赋值运算符,除非左侧是左值。这是一个好主意,尽管不是所有人都同意

    无论如何;当在C++11中添加ref限定符以返回并更改C++03中所有容器的规范时,该建议未被接受(假设它破坏了现有代码)


    std::string
    是在20世纪90年代设计的,在事后看来,它做出了一些今天看来很糟糕的设计选择,但我们仍在坚持。您只需了解
    std::string本身的问题,也许可以在您自己的类中使用ref限定符或视图或其他任何东西来避免它。

    它返回一个字符串值,而不是引用,因此它不应该是可赋值的
    ,您认为这是为什么?不
    字符串str;str=“foo”做同样的事情?当然,但是在
    字符串str中;str=“foo”
    ,变量
    str
    显然是一个左值。临时值。。。不是?我的意思是,
    intn;n=4
    也可以正常工作,但是我上面的第三个示例出现了错误(正如我预期的那样)。是的,它是一个
    xvalue
    ,其生命周期结束于下一行,除非给出一个名称以成为左值。但是xvalue也必须是可修改的。考虑这一点:<代码> CUD.DEIDI>代码> STD::Stry::PosithBuffe<代码>返回> <代码> Value>代码>,您的代码片段格式不正确。@鲁斯兰,假设它是“代码> STD::String::AppEnt/Engult>以支持注释的意图。在任何情况下,可以安全地添加编译器甚至不考虑为这样的事情生成程序集,只是因为它没有任何作用。同意。虽然这是一个优化的问题。优化可能会被关闭,而且无法保证某些优化会发生。它很可能会发生,但若不发生,编译器仍然可以。这根本不能回答我的问题。我理解为什么代码没有做任何事情;我不明白它为什么会编译,因为它的语法是正确的。不管你喜欢与否,C++是用这种方式定义的。@ MarkRansom——这是内置类型和用户定义类型之间的区别。对用户定义类型的操作可能会产生明显的副作用。对右值的成员函数调用一直是合法的。