19

We cannot write int& ref = 40 because we need lvalue on right side. But we can write const int& ref = 40 . Why is this possible? 40 is rvalue instead lvalue

I know that this is an exception but why?

HAL9000
  • 3,562
  • 3
  • 25
  • 47
NewB
  • 344
  • 3
  • 12

4 Answers4

21

As Stroustrup says:

The initializer for a const T& need not be an lvalue or even of type T. In such cases:

[1] First, implicit type conversion to T is applied if necessary.

[2] Then, the resulting value is placed in a temporary variable of type T.

[3] Finally, this temporary variable is used as the value of the initializer.

So, when you type const int& ref = 40, the temporary int variable is created behind the scenes, and ref is bound to this temporary variable.

undermind
  • 1,779
  • 13
  • 33
16

There is a rule in the language that allows binding a const lvalue reference to an rvalue. The main reason for that rule is that, if it was not present, then you would have to provide different overloads of functions to be able to use temporaries as arguments:

class T; // defined somewhere
T f();
void g(T const &x);

With that rule in place you can do g(f()), without it, to be able to do that you would have to create a different g overload that takes an rvalue (and this is from a time where rvalue-references were not even in the language!)

David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I'm still not sure why the [lifetime is extended](http://stackoverflow.com/q/20546981/596781), though :-( – Kerrek SB Apr 03 '14 at 17:57
  • @KerrekSB: I guess the original reason was that this way you could write `T const& _ = ...;` or `T const _ = ...;` indifferently and both would work, the former being more efficient when the expression yielded a reference (no copy). It's quite unfortunate indeed though, because it lets a lot of buts slip in. – Matthieu M. Apr 03 '14 at 17:59
  • 1
    @KerrekSB: I thought I had addressed that in the linked question. That is a side effect of allowing the binding in the general context (not only in function arguments) and not wanting it to cause undefined behavior on every use. Without the lifetime extension: `T const& x = f();` followed by *any* odr-use of `x` would be undefined behavior, raising the question of why allow that in the first place? So either you provide consistency (reference binds in *any* context) with lifetime extension, or you accept inconsistencies and there is no need for lifetime extension. The language did the former. – David Rodríguez - dribeas Apr 03 '14 at 18:03
  • In trying to be concise I completely messed it up. Read *odr-use* above not as *odr-use*, but any use that accesses the real object (lvalue-rvalue conversionm, access to any member...) – David Rodríguez - dribeas Apr 03 '14 at 18:48
  • Hmm, well, I'm still not convinced (as I said in the other post) -- the "end-of-full-expression" rule seems to work just fine, and lifetime extension can be just as surprising (canonical example is passing the reference through another function that returns the reference). So I'm still open to contributions :-) – Kerrek SB Apr 03 '14 at 21:17
  • @KerrekSB: The question is whether inside the body of a function `T const & r = f();` is allowed. The *end-of-full-expression* works fine for a parameter to a function, but not in this case where the expression ends in the semi-colon. If you allow this expression without extending the lifetime any use of `r` will be undefined behavior. – David Rodríguez - dribeas Apr 04 '14 at 13:13
2

why it is possible?

40 is a literal here. Constant references can be initialized with literals and temporaries to extend their life time. This can be done this way by a compiler:

int const& ans = 40;
// transformed:
int __internal_unique_name = 40;
int const& ans = __internal_unique_name;

Another situation is when you have a function, e.g:

void f( std::string const& s);

and you want to call it with

f( "something");

This temporary variable can be only bound to const reference.

4pie0
  • 29,204
  • 9
  • 82
  • 118
  • 1
    That is also the case with non-const references... – David Rodríguez - dribeas Apr 03 '14 at 17:54
  • @DavidRodríguez-dribeas not the second situation provided – 4pie0 Apr 03 '14 at 18:02
  • That is exactly what the compiler will do for you, it maps the call to: `std::string __tmp("something"); f(__tmp);`. That is *because you can* is not a reason to allow this for `const` references (*alone*), as proven by the fact that both Solaris CC and VS have supported binding non-const references to temporaries by doing exactly the same thing. – David Rodríguez - dribeas Apr 03 '14 at 18:06
1

You can bind an rvalue to a const reference. The language guarantees that the bound object lives until the scope of the reference ends and even calls the correct destructor statically. This is e.g. used in a ScopeGuard implementation (http://www.drdobbs.com/cpp/generic-change-the-way-you-write-excepti/184403758?pgno=2) to have virtual-destructor-like behavior without paying for a virtual method call.

Jens
  • 9,058
  • 2
  • 26
  • 43