2

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++?

M.M
  • 138,810
  • 21
  • 208
  • 365
Minimus Heximus
  • 2,683
  • 3
  • 25
  • 50
  • even if the lifetime were extended, this is UB for modifying a const object – M.M Feb 17 '19 at 21:59
  • @M.M I don't see any const object in this program. – aschepler Feb 17 '19 at 22:01
  • 1
    @aschepler when initializing a `const T&` from a prvalue of type `T` , the prvalue is converted to `const T` before materialization of the temporary object (C++17 dcl.init.ref/5.2.1.2 for built-in type, /5.2.2.2 for class type) – M.M Feb 17 '19 at 22:07
  • 1
    @nano: I've deleted my answer. It needs more expertise to give a good answer on this. Check out: https://stackoverflow.com/questions/8245027/am-i-right-in-saying-that-const-cast-followed-by-modification-on-a-ref-to-const. This is an old question (C++11), but it sheds some light on the issue. It seems that this is more complicated (and maybe it matters whether the type is class or non-class, as you suspected). – geza Feb 17 '19 at 22:10
  • The lifetime issue was discussed [here](https://stackoverflow.com/questions/29162620/const-reference-to-temporary-reference) (the standard isn't clear on the lifetime of the temporary created by casting a prvalue to reference type) although (a) it's not clear to me how the proposed resolution of DR1376 actually addresses the issue, and (b) C++17 doesn't contain the resolution anyway – M.M Feb 17 '19 at 22:33
  • @M.M _it's not clear to me how the proposed resolution of DR1376 actually addresses the issue_ The issue was addressed in [CWG1299](http://wg21.link/cwg1299) and [is already merged](https://github.com/cplusplus/draft/commit/526e493f5fb8b599f1cd8aed4220bc1a0ece5fe6#diff-9a24d758fe74524a5a0f33f97735790d) – Language Lawyer Feb 17 '19 at 22:42
  • @LanguageLawyer convenient, I had found that from searching the defects list at the same instant you posted the comment :) – M.M Feb 17 '19 at 22:51
  • @geza OK. But I hope some clear answer is posted. – Minimus Heximus Feb 17 '19 at 22:58

1 Answers1

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 initialization T t(e); for some invented temporary variable t (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, or reinterpret_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.

M.M
  • 138,810
  • 21
  • 208
  • 365
  • Is a temporary with type `int` an "object"? I'm used to assume "objects" are not of the fundamental types, while "expressions" can be of fundamental types. That clause is about `object`s; why the word `expression` is not used instead? – Minimus Heximus Feb 18 '19 at 01:22
  • 1
    @nano yes, it is a temporary object of type `const int`. You are thinking of the distinction between *object of class type* and *object of non-class type* (the latter is sometimes called *object of built-in type*). Also objects are not expressions and expressions are not objects. – M.M Feb 18 '19 at 02:04
  • Can you give me an example of an object which is not an expression? – Minimus Heximus Feb 18 '19 at 03:21
  • 1
    @nano all objects are not expressions. An object is something that exists in memory (in the abstract machine); an expression is a syntactic construct in the source code – M.M Feb 18 '19 at 03:33