views:

217

answers:

4

Hi,

I cannot initialize a non-const reference to type T1 from a convertible type T2. However, I can with a const reference.

long l;
const long long &const_ref = l; // fine
long long &ref = l;             // error: invalid initialization of reference of
                                // type 'long long int&' from expression of type
                                // 'long int'

Most problems I encountered were related to r-values that cannot be assigned to a non-const reference. This is not the case here -- can someone explain? Thanks.

+6  A: 

An integer promotion results in an rvalue. long can be promoted to a long long, and then it gets bound to a const reference. Just as if you had done:

typedef long long type;
const type& x = type(l); // temporary!

Contrarily an rvalue, as you know, cannot be bound to a non-const reference. (After all, there is no actual long long to refer to.)

GMan
I found what I was missing: "The C++ Standard section 3.10:5 indicates that the result of calling a function that does not return a reference is an rvalue. [The result of `type(l)` **is** an rvalue.] According to section 13.3.3.1.4:3 a standard conversion sequence cannot be formed if it requires binding a non-const reference to an rvalue, except for implicit object parameters." (Found on IBM.com)
Julien L.
@Julien not that you think i'm a pedant-retard, but your finding is not relevant here because `l` is not an rvalue. It's an lvalue. No `type(l)` appears in your example and such a cast is not done implicit either in any way (there, on the language level, is no integer promotion or conversion happening here. The binding just fails because there are no rules for it). Also, `type(l)` is not a function call, but a functional cast expression. Still i agree that `type(l)` is an rvalue.
Johannes Schaub - litb
+2  A: 

long long is not necessarily sized equal to long and may even use an entire different internal representation. Therefor you cannot bind a non-const reference to long to an object of type long long or the other way around. The Standard forbids it, and your compiler is correct to not allow it.

You can wonder the same way about the following code snippet:

long a = 0; 
long long b = 0;

a = b; // works!

long *pa = 0;
long long *pb = pa;

The last initialization won't work. Just because a type is convertible to another one, doesn't mean another type that compounds one of them is convertible to a third type that compounds the other one. Likewise, for the following case

struct A { long i; };
struct B { long long i; };

A a;
B b = a; // fail!

In this case A and B each compound the type long and long long respectively, much like long& and long long& compound these types. However they won't be convertible into each other just because of that fact. Other rules happen to apply.

If the reference is to const, a temporary object is created that has the correct type, and the reference is then bound to that object.

Johannes Schaub - litb
Are you sure that the size and representation has much to do with it? I can't initialize neither a signed or unsigned char reference with a variable of type char either, even though one of them must be pretty much identical to char. I thought it was simply a rule to patch up a hole in the type system of C (where this appears to be allowed for pointers) to increase type safety.
UncleBens
Julien L.
@Julien, the reasoning is the same. Just because you can convert `float` to `int` does not mean you can convert `float` to `int*`.
Johannes Schaub - litb
@UncleBens, correct. Same size and representation isn't sufficient for the conversion to succeed. What's sufficient is that the same type/class or a derived class or a compatibly qualified type of these is used in initialization. For non-class types that means size and representations are the same. So it's necessary but not at all sufficient to have the same size and/or representation as you show in your example.
Johannes Schaub - litb
Julien L.
@Julien as i say at the last sentence in my answer, this is a rule in the Standard that makes it possible. But there is no rule that allows your 3'rd line.
Johannes Schaub - litb
A: 

I'm not a standards lawyer, but I think this is because long long is wider than long. A const reference is permitted because you won't be changing the value of l. A regular reference might lead to an assignment that's too big for l, so the compiler won't allow it.

Kristo
A: 

Let's assume that it's possible :

 long long &ref = l; 

It means that later in the code you can change the value referenced by ref to the value that is bigger then long type can hold but ok for long long. Practically, it means that you overwrite extra bytes of memory which can be used by different variable with unpredictable results.

a1ex07