正在通过 const ref 未定义行为捕获新构造的对象
Is capturing a newly constructed object by const ref undefined behavior
以下(人为的示例)是否正常,或者是否为未定义的行为:
// undefined behavior?
const auto& c = SomeClass{};
// use c in code later
const auto& v = c.GetSomeVariable();
很安全。 const ref 延长了 temporary 的生命周期。范围将是 const ref 的范围。
The lifetime of a temporary object may be extended by binding to a
const lvalue reference or to an rvalue reference (since C++11), see
reference initialization for details.
Whenever a reference is bound to a temporary or to a subobject
thereof, the lifetime of the temporary is extended to match the
lifetime of the reference, with the following exceptions:
- a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of
the return expression. Such function always returns a dangling
reference.
- a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as
long as the object exists. (note: such initialization is ill-formed as
of DR 1696).
- a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function
call: if the function returns a reference, which outlives the full
expression, it becomes a dangling reference.
- a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing
that new-expression, not as long as the initialized object. If the
initialized object outlives the full expression, its reference member
becomes a dangling reference.
- a temporary bound to a reference in a reference element of an aggregate initialized using direct-initialization syntax (parentheses)
as opposed to list-initialization syntax (braces) exists until the end
of the full expression containing the initializer.
struct A {
int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
In general, the lifetime of a temporary cannot be further extended by "passing it
on": a second reference, initialized from the reference to which the
temporary was bound, does not affect its lifetime.
正如 @Konrad Rudolph 所指出的(见上文最后一段):
"If c.GetSomeVariable()
returns a reference to a local object or a reference that it is itself extending some object’s lifetime, lifetime extension does not kick in"
感谢lifetime extension,这里应该没有问题。新构造的对象将一直存在,直到引用超出范围。
是的,这是绝对安全的:绑定到 const
引用将临时对象的生命周期延长到该引用的范围。
请注意,该行为不是 可传递的。例如,用
const auto& cc = []{
const auto& c = SomeClass{};
return c;
}();
cc
悬垂。
这是安全的。
[class.temporary]/5
: There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. [..]
[class.temporary]/6
: The third context is when a reference is bound to a temporary object. The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following: [lots of things here]
在这种情况下是安全的。但是请注意,并非所有临时对象都可以通过 const 引用安全地捕获...例如
#include <stdio.h>
struct Foo {
int member;
Foo() : member(0) {
printf("Constructor\n");
}
~Foo() {
printf("Destructor\n");
}
const Foo& method() const {
return *this;
}
};
int main() {
{
const Foo& x = Foo{}; // safe
printf("here!\n");
}
{
const int& y = Foo{}.member; // safe too (special rule for this)
printf("here (2)!\n");
}
{
const Foo& z = Foo{}.method(); // NOT safe
printf("here (3)!\n");
}
return 0;
}
为 z
获得的引用使用起来不安全,因为在到达 printf
语句之前,临时实例将在完整表达式结束时被销毁。输出为:
Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!
以下(人为的示例)是否正常,或者是否为未定义的行为:
// undefined behavior?
const auto& c = SomeClass{};
// use c in code later
const auto& v = c.GetSomeVariable();
很安全。 const ref 延长了 temporary 的生命周期。范围将是 const ref 的范围。
The lifetime of a temporary object may be extended by binding to a const lvalue reference or to an rvalue reference (since C++11), see reference initialization for details.
Whenever a reference is bound to a temporary or to a subobject thereof, the lifetime of the temporary is extended to match the lifetime of the reference, with the following exceptions:
- a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of the return expression. Such function always returns a dangling reference.
- a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as long as the object exists. (note: such initialization is ill-formed as of DR 1696).
- a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function call: if the function returns a reference, which outlives the full expression, it becomes a dangling reference.
- a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing that new-expression, not as long as the initialized object. If the initialized object outlives the full expression, its reference member becomes a dangling reference.
- a temporary bound to a reference in a reference element of an aggregate initialized using direct-initialization syntax (parentheses) as opposed to list-initialization syntax (braces) exists until the end of the full expression containing the initializer.
struct A { int&& r; }; A a1{7}; // OK, lifetime is extended A a2(7); // well-formed, but dangling reference
In general, the lifetime of a temporary cannot be further extended by "passing it on": a second reference, initialized from the reference to which the temporary was bound, does not affect its lifetime.
正如 @Konrad Rudolph 所指出的(见上文最后一段):
"If
c.GetSomeVariable()
returns a reference to a local object or a reference that it is itself extending some object’s lifetime, lifetime extension does not kick in"
感谢lifetime extension,这里应该没有问题。新构造的对象将一直存在,直到引用超出范围。
是的,这是绝对安全的:绑定到 const
引用将临时对象的生命周期延长到该引用的范围。
请注意,该行为不是 可传递的。例如,用
const auto& cc = []{
const auto& c = SomeClass{};
return c;
}();
cc
悬垂。
这是安全的。
[class.temporary]/5
: There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. [..]
[class.temporary]/6
: The third context is when a reference is bound to a temporary object. The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following: [lots of things here]
在这种情况下是安全的。但是请注意,并非所有临时对象都可以通过 const 引用安全地捕获...例如
#include <stdio.h>
struct Foo {
int member;
Foo() : member(0) {
printf("Constructor\n");
}
~Foo() {
printf("Destructor\n");
}
const Foo& method() const {
return *this;
}
};
int main() {
{
const Foo& x = Foo{}; // safe
printf("here!\n");
}
{
const int& y = Foo{}.member; // safe too (special rule for this)
printf("here (2)!\n");
}
{
const Foo& z = Foo{}.method(); // NOT safe
printf("here (3)!\n");
}
return 0;
}
为 z
获得的引用使用起来不安全,因为在到达 printf
语句之前,临时实例将在完整表达式结束时被销毁。输出为:
Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!