虽然我以为我理解了C ++中的rvalue
和lvalue
语义,但我似乎一遍又一遍地嘲笑那些向我证明我不知道蹲下的奇怪例子。
然而,有两个非常简单和基本的,我不明白它们是如何工作的。在我编译它们之前,我认为没有什么是好的,在我看到(1)
有效之后,我认为(2)
也会起作用。但是,(1)
有效,(2)
没有:
(1) const std::string &s = "asd";
这里发生了什么?我的猜测是,const string
构造了一个临时"asd"
对象,然后s
绑定到该临时对象。但是不会在这一行之后立即销毁临时对象,所以我们会留下无效的引用吗?
当我放弃const
限定符时:
(2) std::string &s = "asd";
我收到编译错误(VS 2013):cannot convert from 'const char [4]' to 'std::string &'
。这似乎反驳了我的理论,因为,根据它(我的猜测),一个临时的string
对象将由"asd"
构建,然后s
分配给它,这将不会生成任何编译错误。
总结一下:
s
绑定的是什么?s
绑定的对象的生命周期是什么?(1)
编译而(2)
没有(是std::string
中定义的转换运算符还是C ++语义?)答案 0 :(得分:3)
(2)
不起作用,因为"asd"
是char const[]
。编译器会将其(实际上)隐式转换为临时string
rvalue。您不能将非const引用分配给将立即超出范围的值。所以你的分析是正确的。
对于(1)
我自己必须做一点查询,但我想我已经找到了答案:Does a const reference prolong the life of a temporary?
标准允许在将临时绑定到const引用时发生复制。 Unexpexted,我同意:)所以会再次隐式转换为临时string
rvalue,并且因为引用现在是const
,所以这个rvalue将被复制,因此它会持续存在。
答案 1 :(得分:2)
- 什么是绑定?
- s绑定到的对象的生命周期是什么?
到新创建的临时std::string
,生命只要s
。
是什么使(1)编译而(2)不是?
您不能对非左值进行非const引用。
引自8.5.3 [dcl.init.ref]:
对类型“
cv1 T1
”的引用由类型的表达式初始化 “cv2 T2
”如下:
- [..剪辑,不相关..]
- 否则[如果右侧不是左值],则引用应为非易失性const类型的左值引用(即
cv1
应为const
),或引用应为右值参考。
- [..剪辑,不相关..]
- 否则,将创建一个临时类型为“
cv1
T1”并使用初始化表达式初始化 非参考拷贝初始化的规则(8.5)。参考资料 然后绑定到临时。如果T1与T2引用相关,则为cv1 应具有相同的cv资格,或更高的cv资格 比,cv2。如果T1与T2参考相关且参考是a 右值引用,初始化表达式不应是左值。除了最后一个之外的所有情况(即创建和初始化a 暂时从初始化表达式),引用据说 直接绑定到初始化表达式。
在您的情况下,您需要将const char*
转换为std::string
,这会引入类型为std::string
的临时文件。由于这不是左值,因此引用应为const
。这就结束了为什么(2)不起作用。
终生,请看12.2 [class.temporary]:
有两种情况下,临时表在与完整表达结束时不同的点被销毁。 [...]
第二个上下文是引用绑定到临时的。引用绑定的临时对象或引用所绑定的子对象的完整对象的临时对象的生命周期仍然存在[...]
这意味着只要您的const
参考,临时就会。这就结束了为什么(1)确实有效。
请注意,标准中有更多细节,其中包括一些极端情况,因此您可能希望完全阅读这些部分。
答案 2 :(得分:2)
s
绑定的内容是什么?
对于从字符串文字构造的临时std::string
,正如您所推测的那样。
s
绑定到的对象的生命周期是什么?
延长临时生命周期以匹配参考文献。通常,临时表在创造它们的全表达结束时被破坏;这是该规则的一个例外。
是什么使(1)编译而(2)不是?
另一种语言规则:临时工具不能绑定到非const
引用。这个规则有点古怪,但很有用:如果你不小心修改了一个临时的而不是一个更持久的对象,它可以防止出现细微的错误。