问题
In the following program:
int Func()
{
int a = { 10 };
return a;
}
int main()
{
int& r = (int&)(const int&)Func();
r = 5;
}
r
is a reference to a temporary of type int
. But temporaries are destroyed immediately unless they are assigned normally to a reference. The assignment above does not seem normal. Is it safe to use r
in Standard C++?
回答1:
Introduction: the C-style casts are equivalent to (C++17 [expr.cast]):
int& r = const_cast<int&>( static_cast<const int&>(Func()) );
In the subexpression static_cast<const int&>(Func())
the behaviour is described by C++17 [expr.static.cast]/4 (where T
is the type being cast to):
If
T
is a reference type, the effect is the same as performing the declaration and initializationT t(e);
for some invented temporary variablet
(11.6) and then using the temporary variable as the result of the conversion
In this case T
is const int&
, so the initialization of the reference is similar to const int& t(Func());
.
Now there's two issues in this code:
- The type of the temporary
- The lifetime of the temporary involved
The type of the temporary is const int
(C++17 [dcl.init.ref]/5.2.1.2). So your code causes undefined behaviour by modifying a const object. Link to other SO question on this topic
For the rest of this answer (addressing the lifetime issue) I'll assume you change r = 5
to some statement that only reads r
.
The behaviour of the reference chaining changed with the application of CWG 1299. The defect was filed in April 2011 and resolved in March 2017. The resolution did not appear in C++17 (N4659); it appears only in post-C++17 drafts.
The resolution has status DRWP, my understanding of this is that it means it retroactively applies to C++17 but not to C++14 (if someone wants to confirm or correct this that would be great).
Anyway, this resolution enables lifetime extention over reference chains in some circumstances. The wording is (N4762 class.temporary/6):
[...] 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:
- [...]
- a
const_cast
,static_cast
,dynamic_cast
, orreinterpret_cast
converting, without a user-defined conversion, a glvalue operand that is one of these expressions to a glvalue that refers to the object designated by the operand, or to its complete object or a subobject thereof,
Prior to CWG1299 this paragraph only applied to initializing a reference from a prvalue, but now it can apply to cases of initializing a reference from any expression category if the designated object is a temporary object.
Note that the way temporary materialization works in C++17 is that the prvalue is converted to an xvalue when materialization happens, and this xvalue is the glvalue referred to by the bold text above.
There is even an example included now to confirm this:
const int& b = static_cast<const int&>(0);
// temporary int has same lifetime as b
The behaviour of compilers shown in another deleted answer must be applying the resolution of CWG1299.
来源:https://stackoverflow.com/questions/54736807/is-casting-a-temporary-with-type-int-to-a-reference-safe