Ast*_*ngs 5 c++ language-lawyer c++17
考虑:
void foo(std::string& s);
Run Code Online (Sandbox Code Playgroud)
在这个函数中,表达式s是左值 std::string(不是 std::string&),因为引用在表达式中并不真正“存在”:
[expr.type/1]:如果表达式最初具有“引用T”类型([dcl.ref],[dcl.init.ref]),则T在任何进一步分析之前将类型调整为。表达式指定引用所表示的对象或函数,表达式是左值或 x 值,具体取决于表达式。[..]
现在考虑:
const std::string& foo(const std::string& s1, const std::string& s2)
{
return (s1.size() < s2.size() ? s1 : s2);
}
Run Code Online (Sandbox Code Playgroud)
关于这里的条件运算符是否涉及创建临时对象的另一个问题存在争论(然后对foo作为悬空引用的返回值产生影响)。
我的解释是,是的,它必须,因为:
[expr.cond/5]: 如果第二个和第三个操作数是同一个值类别的泛左值并且具有相同的类型,则结果属于那个类型和值类别,如果第二个或第三个操作数是位域,则结果是位域,或者如果两者都是位域。
和:
[expr.cond/7.1]:第二个和第三个操作数的类型相同;结果属于该类型,结果对象使用选定的操作数进行初始化。
std::string从 a初始化 astd::string涉及一个副本。
然而,我很惊讶 GCC 没有对悬空引用发出警告。调查中,我发现foo确实不传播所选参数参考语义:
#include <string>
#include <iostream>
using std::string;
using std::cout;
void foo(string& s1, string& s2)
{
auto& s3 = (s1.size() < s2.size() ? s1 : s2);
s3 = "what";
}
int main()
{
string s1 = "hello";
string s2 = "world";
foo(s1, s2);
cout << s1 << ' ' << s2 << '\n'; // Output: hello what
}
Run Code Online (Sandbox Code Playgroud)
s2通过引用传递到的原始foo已由条件运算符选择,然后绑定到s3并进行修改。没有任何复制的证据。
这与我对表达式如何工作以及条件运算符如何工作的理解不符。
那么,我上面的哪些陈述是不正确的,为什么?
由于似乎有些混乱,因此我在下面绘制了我的理解所说的事件链。我意识到这是错误的——我上面的测试用例证明了这一点。但我想确切地了解为什么。理想情况下,我想要一些标准的措辞,而不仅仅是“你错了”。我已经知道我错了。这就是为什么我要问。
const std::string(不是引用!)const std::string也是const std::string,所以它是const std::string从 a 初始化的const std::string