0
int main(int argc, char const *argv[])
{    
   int *i;
   const int * &j = i;
}

This code gives the error error: non-const lvalue reference to type 'const int *' cannot bind to a value of unrelated type 'int *

But it's not clear to me why this is not allowed. I defined j to be a reference to a pointer to a constant int. So if I set it equal to i, aren't I saying that j is a reference of i and we are not allowed to modify the int pointed to by i through j?

user5965026
  • 465
  • 5
  • 16
  • But i is not const... – MrBens Apr 24 '20 at 16:23
  • @MrBens I know, but is that relevant? – user5965026 Apr 24 '20 at 16:26
  • Well when you write `const int * &j` you should assign to it a `const int *`. It really depands what you're trying to achive here. – MrBens Apr 24 '20 at 16:28
  • Hmm, but if you drop the reference, you can assign a `const int *` to a `int *` with no issue right? If so, why does having the reference change things? – user5965026 Apr 24 '20 at 16:29
  • For the same reason as [const ptr ptr conversion](https://isocpp.org/wiki/faq/const-correctness#constptrptr-conversion), although with a reference rather than a pointer-pointer. The simple fix is `const int * const&j = i;` – Eljay Apr 24 '20 at 16:30
  • Does this answer your question? [Why isn't it legal to convert "pointer to pointer to non-const" to a "pointer to pointer to const"](https://stackoverflow.com/questions/2220916/why-isnt-it-legal-to-convert-pointer-to-pointer-to-non-const-to-a-pointer-to) – JaMiT Apr 24 '20 at 16:52

1 Answers1

1

So if I set it equal to i, aren't I saying that j is a reference of i

That's what you're trying to say. But the type of i is incompatible, so that's something that cannot be said.

and we are not allowed to modify the int pointed to by i through j?

If you had a pointer to const, then you couldn't modify the pointed int. And you could refer to such pointer with j. But you haven't created such pointer.

A pointer to non-const is convertible to pointer to const however. But result of such conversion is an rvalue, so your reference to non-const cannot be bound to it. If you used a reference to const, it would extend the lifetime of the temporary result of the implicit conversion:

const int * const &j = i;

But the reference just adds unncecessary cofusion. Better give the converted pointer a name, rather than refer to a temprary object:

const int* j = i;

Why is the type of i not compatible?

Because the language rules say so.

Is this just the rule?

This is the rule (from latest standard draft):

Given types “cv1 T1” and “cv2 T2”, “cv1 T1” is reference-related to “cv2 T2” if T1 is similar ([conv.qual]) to T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if a prvalue of type “pointer to cv2 T2” can be converted to the type “pointer to cv1 T1” via a standard conversion sequence ([conv]). In all cases where the reference-compatible relationship of two types is used to establish the validity of a reference binding and the standard conversion sequence would be ill-formed, a program that necessitates such a binding is ill-formed.

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:

  • If the reference is an lvalue reference and the initializer expression

    • is an lvalue (but is not a bit-field), and “cv1 T1” is reference-compatible with “cv2 T2”, or

    • has a class type (i.e., T2 is a class type), where T1 is not reference-related to T2, and can be converted to an lvalue of type “cv3 T3”, where “cv1 T1” is reference-compatible with “cv3 T3”92 (this conversion is selected by enumerating the applicable conversion functions ([over.match.ref]) and choosing the best one through overload resolution),

then the reference is bound to the initializer expression lvalue in the first case and to the lvalue result of the conversion in the second case (or, in either case, to the appropriate base class subobject of the object).

  • Otherwise, if the reference is an lvalue reference to a type that is not const-qualified or is volatile-qualified, the program is ill-formed.
eerorika
  • 232,697
  • 12
  • 197
  • 326
  • Ah. I'm trying to wrap my head around this. Why is the type of `i` not compatible? Is this just the rule? – user5965026 Apr 24 '20 at 16:28
  • @user5965026 Because `const int*` and `int*` not the same type. They are not even same type when the top level cv-qualifiers are ignored (both are non-const). Neither are they related in an inheritance hierarchy. – eerorika Apr 24 '20 at 16:32
  • Ahh I think I just realized this situation is analogous to trying to set a non-const reference to an r-value. What I didn't recognize is that the RHS in the current case isn't an l-value but an r-value because of the `int *` to `const int *` implicit conversion. Is that the idea? In addition, when you put the extra `const` in there `const int * const &j = i;` that's analogous to `const int &j = some_r_value` and is valid, but `int &j = some_r_value` is not because `some_r_value` is a temporary – user5965026 Apr 24 '20 at 16:56
  • @user5965026 Yes. `i` by itself is an lvalue. But is not an lvalue that the reference can be bound to because of the wrong type. The reference could be bound to the result of the implicit conversion if it wasn't non-const because the result of that implicit conversion is an rvalue i.e. for the same reason as that example. – eerorika Apr 24 '20 at 17:10