当它绑定到调用函数中的 const 引用时,它的返回值的生命周期如何扩展到调用函数的范围?
Posted
技术标签:
【中文标题】当它绑定到调用函数中的 const 引用时,它的返回值的生命周期如何扩展到调用函数的范围?【英文标题】:How is its lifetime of a return value extended to the scope of the calling function when it is bound to a const reference in the calling function? 【发布时间】:2011-02-06 14:15:34 【问题描述】:“如果你从函数返回一个值(不是引用),然后将它绑定到调用函数中的 const 引用,它的生命周期将扩展到调用函数的范围。”
所以:案例 A
const BoundingBox Player::GetBoundingBox(void)
return BoundingBox( &GetBoundingSphere() );
从函数GetBoundingBox()
返回一个const BoundingBox
类型的值
变体 I:(将其绑定到 const 引用)
const BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();
变体 II:(将其绑定到 const 副本)
const BoundingBox l_Bbox = l_pPlayer->GetBoundingBox();
两者都工作正常,我没有看到 l_Bbox
对象超出范围。 (虽然,我理解在变体一中,复制构造函数没有被调用,因此比变体 II 稍好)。
另外,为了比较,我做了以下更改。
案例 B
BoundingBox Player::GetBoundingBox(void)
return BoundingBox( &GetBoundingSphere() );
带有变体: 我
BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();
和二:
BoundingBox l_Bbox = l_pPlayer->GetBoundingBox();
对象l_Bbox
仍然没有超出范围。 “将它绑定到调用函数中的 const 引用,它的生命周期会扩展到调用函数的范围”,如何真正将对象的生命周期扩展到调用函数的范围?
我在这里错过了一些琐碎的事情吗?
【问题讨论】:
它似乎适用于您的特定编译器(也就是说,btw ...合法。 @Peter:我正在使用 MSVC 2008。让我无法理解的是为什么/如何 const 对延长寿命有任何影响。在我看来,它可以防止任何人对对象进行更改(常量正确性等),就是这样.. 你的变体 I 看起来有点滑稽 - 你的编译器真的接受它而不抱怨吗?看起来您正在使用右值初始化对非常量的引用,这是不允许的。 @Mike:你说的是案例 A.1 还是案例 B.1 变体? @brainydexter:就您的第一条评论而言-延长生命周期的不是“const”,而是参考。临时对象不应该能够绑定到非常量引用(因为临时对象是右值),但显然 MSVC 在这方面有点过于宽容了。 【参考方案1】:首先,临时对象的生命周期被延长到绑定到它的 const 引用的生命周期,而不是“到调用函数的范围”(尽管也许这就是你所说的那个奇怪的措辞“调用的范围”的意思功能”)。这就是您的CASE A
说明的内容,您将 const 引用附加到临时对象。只要参考存在,临时对象就会继续存在。当引用结束其生命周期时,临时对象也会被销毁。
其次,您的 CASE B
格式不正确,不可编译。也就是说,
BoundingBox& l_Bbox = l_pPlayer->GetBoundingBox();
是非法的。在 C++ 中,将非常量引用附加到临时对象是非法的。如果你的编译器允许,它一定是你的编译器的一个怪癖/扩展,这与 C++ 语言关系不大。
【讨论】:
【参考方案2】:通常一个临时对象(例如由函数调用返回的对象)的生命周期会延伸到“封闭表达式”的末尾。但是,临时绑定到引用通常会将其生命周期“提升”到引用的生命周期(可能是也可能不是调用函数的生命周期),但也有一些例外。 12.2/5“临时对象”中的标准涵盖了这一点:
引用绑定到的临时对象或作为临时对象绑定的子对象的完整对象的临时对象将在引用的生命周期内持续存在,除非下面指定。临时绑定到构造函数的 ctor-initializer (12.6.2) 中的引用成员,直到构造函数退出。在函数调用 (5.2.2) 中对引用参数的临时绑定一直存在,直到包含该调用的完整表达式完成为止。
有关详细信息,请参阅以下内容:
C++ constant reference lifetime (container adaptor) GotW #88: A Candidate For the "Most Important const"一个可能有助于可视化正在发生的事情的示例:
#include <iostream>
#include <string>
class foo
public:
foo( std::string const& n) : name(n)
std::cout << "foo ctor - " << name + " created\n";
;
foo( foo const& other) : name( other.name + " copy")
std::cout << "foo copy ctor - " << name + " created\n";
;
~foo()
std::cout << name + " destroyed\n";
;
std::string getname() const return name; ;
foo getcopy() const return foo( *this); ;
private:
std::string name;
;
std::ostream& operator<<( std::ostream& strm, foo const& f)
strm << f.getname();
return strm;
int main()
foo x( "x");
std::cout << x.getcopy() << std::endl;
std::cout << "note that the temp has already been destroyed\n\n\n";
foo const& ref( x.getcopy());
std::cout << ref << std::endl;
std::cout << "the temp won't be deleted until after this...\n\n";
std::cout << "note that the temp has *not* been destroyed yet...\n\n";
显示:
foo ctor - x created
foo copy ctor - x copy created
x copy
x copy destroyed
note that the temp has already been destroyed
foo copy ctor - x copy created
x copy
the temp won't be deleted until after this...
note that the temp has *not* been destroyed yet...
x copy destroyed
x destroyed
【讨论】:
【参考方案3】:关键是,当按值返回时,该值被复制到您分配函数结果的变量中。 (就像你说的 - 复制构造函数被调用)。无需延长生命周期,您只需创建一个全新的对象。
当通过引用返回时,您只需将指针传递给函数中定义的变量。因此,不会创建一个新对象,您只需在函数外部引用它。在这种情况下,函数内部变量的生命周期会延长。
【讨论】:
【参考方案4】:通常,如果你从一个函数中按值返回一个对象,当赋值表达式完成时,该对象将被销毁:
myclass X = getX(); // after copy constructor, the returned value is destroyed
// (but you still hold a copy in X)
在你描述的情况下,返回的值稍后会被销毁,允许你使用它:
const myclass& X = getX();
cout << X.a << endl; // still can access the returned value, it's not destroyed
【讨论】:
以上是关于当它绑定到调用函数中的 const 引用时,它的返回值的生命周期如何扩展到调用函数的范围?的主要内容,如果未能解决你的问题,请参考以下文章
为啥在特殊成员函数中将 r 值绑定到 const 左值引用是非法的?
为啥我不能将 const 左值引用绑定到返回 T&& 的函数?
在其他容器中使用 boost::container::static_vector 时,gcc 编译错误“将‘const s’绑定到‘s&’类型的引用丢弃限定符”