与 const 引用关联的临时对象的生命周期(方法链接)
Lifetime of temporary object associated with const reference (method chaining)
考虑以下代码片段:
#include <iostream>
struct S {
~S() { std::cout << "dtor\n"; }
const S& f(int i) const { std::cout << i << "\n"; return *this; }
};
int main() {
const S& s = S();
s.f(2);
}
2
dtor
即对象生命周期通过引用扩展,这在 Herb 的 article.
中有解释
但是,如果我们只更改一行代码并写成:
const S& s = S().f(1);
对已销毁对象的 f(2)
调用:
1
dtor
2
为什么会这样? f()
的 return 值不是 "temporality" 的正确类型吗?
Why did this happen? Is f()
's return value not a correct type of "temporality"?
对,不是。这是最近有点争议的问题:"temporality"的官方定义有点开放。
在最近的编译器中,时间性一直在扩大。首先,它仅适用于纯右值(非"reference")表达式,成员访问("dot operator")适用于此类表达式。现在它也适用于转换表达式和数组访问。虽然您可以将移动操作写成 static_cast< T && >( t )
,这将保留时间性,但简单地写成 std::move( t )
不会。
我正在研究 series of proposals 以扩展 C++,因此您的示例将按您的预期工作。该功能出现在 C++17 中的可能性非零。
当你这样写一个函数时...
const S& f(int i) const { std::cout << i << "\n"; return *this; }
...您正在指示编译器 return a const S&
并且您负责确保引用的对象具有适合调用者使用的生命周期。 ("ensuring" 可能构成记录与您的设计一起正常工作的客户端使用情况。)
通常 - 将代码典型地分为头文件和实现文件 - f(int) const
的实现甚至对调用代码都不可见,在这种情况下,编译器不知道 S
一个引用可能被 returned,也不知道那个 S
是否是临时的,所以它没有决定是否需要延长生命周期的基础。
除了显而易见的选项(例如,信任客户端编写安全代码,return按值或智能指针),值得了解一个更隐蔽的选项...
const S& f(int i) const & { ...; return *this; }
const S f(int i) const && { ...; return *this; }
函数体重载 f
之前的 &
和 &&
如果 *this
是可移动的,则使用 &&
版本,否则&
版本被使用。这样,有人将 const &
绑定到 f(...)
调用过期对象将绑定到对象的新副本,并根据本地 const
引用延长生命周期,而当对象还没有过期 const
引用将指向原始对象(只要引用仍然不能保证存在 - 需要一些谨慎)。
考虑以下代码片段:
#include <iostream>
struct S {
~S() { std::cout << "dtor\n"; }
const S& f(int i) const { std::cout << i << "\n"; return *this; }
};
int main() {
const S& s = S();
s.f(2);
}
2
dtor
即对象生命周期通过引用扩展,这在 Herb 的 article.
中有解释但是,如果我们只更改一行代码并写成:
const S& s = S().f(1);
对已销毁对象的 f(2)
调用:
1
dtor
2
为什么会这样? f()
的 return 值不是 "temporality" 的正确类型吗?
Why did this happen? Is
f()
's return value not a correct type of "temporality"?
对,不是。这是最近有点争议的问题:"temporality"的官方定义有点开放。
在最近的编译器中,时间性一直在扩大。首先,它仅适用于纯右值(非"reference")表达式,成员访问("dot operator")适用于此类表达式。现在它也适用于转换表达式和数组访问。虽然您可以将移动操作写成 static_cast< T && >( t )
,这将保留时间性,但简单地写成 std::move( t )
不会。
我正在研究 series of proposals 以扩展 C++,因此您的示例将按您的预期工作。该功能出现在 C++17 中的可能性非零。
当你这样写一个函数时...
const S& f(int i) const { std::cout << i << "\n"; return *this; }
...您正在指示编译器 return a const S&
并且您负责确保引用的对象具有适合调用者使用的生命周期。 ("ensuring" 可能构成记录与您的设计一起正常工作的客户端使用情况。)
通常 - 将代码典型地分为头文件和实现文件 - f(int) const
的实现甚至对调用代码都不可见,在这种情况下,编译器不知道 S
一个引用可能被 returned,也不知道那个 S
是否是临时的,所以它没有决定是否需要延长生命周期的基础。
除了显而易见的选项(例如,信任客户端编写安全代码,return按值或智能指针),值得了解一个更隐蔽的选项...
const S& f(int i) const & { ...; return *this; }
const S f(int i) const && { ...; return *this; }
函数体重载 f
之前的 &
和 &&
如果 *this
是可移动的,则使用 &&
版本,否则&
版本被使用。这样,有人将 const &
绑定到 f(...)
调用过期对象将绑定到对象的新副本,并根据本地 const
引用延长生命周期,而当对象还没有过期 const
引用将指向原始对象(只要引用仍然不能保证存在 - 需要一些谨慎)。