1
void test(const int*& in){}

int main(){
 int a = 5; 
 test(&a);
 return 0;
}

Above code does not compile saying cannot bind non-const lvalue reference of type 'const int*&' to an rvalue of type 'const int*'. It does work as expected if I make this change:

 int a = 5;
 const int *b = &a; // `const` has to be specified here. why?
 test(b);

I have 2 questions:

1- Why doesn't my first example work? if a regular constant can be passed like this:

void test(const int& in){}

int main(){
 test(5);
 return 0;
}

5 doesn't have a reference, C++ does the work behind the scene to create a temp variable for me. Why isn't it doing the same in the case of passing a pointer by reference?

2- Why do I have to specify const above before int *b for it to compile? since I don't have to put it in the case of a regular integer reference? i.e this works fine:

void test(const int& in){}

int main(){
 int a = 5;  // no `const` added.
 test(a);
 return 0;
}
Dan
  • 2,694
  • 1
  • 6
  • 19
  • "Above code does not compile saying &a is not a reference" nope. Read the compiler error more carefully and please include it in the quesiton – 463035818_is_not_an_ai May 27 '21 at 17:06
  • 1
    I have modified it. – Dan May 27 '21 at 17:08
  • 1
    The subtlety here is that `const int&` is a `const` reference to an `int`, but `const int*&` is a non-`const` reference to a `const int*`, which is itself the type "pointer to `const int`". You can make the latter reference itself `const` by making the argument `const int* const& in`, although there's very little reason to do that instead of just `const int* in` and taking the argument by value. – Nathan Pierson May 27 '21 at 17:21

2 Answers2

3

1- Why doesn't my first example work?

The parameter is a reference to a pointer to const. &a - which is the argument - is a pointer to non-const. Pointer to const and pointer to non-const are different types. The argument could be implicitly converted to a pointer to const type. However, the result of that conversion is an rvalue - it is a temporary object. The parameter of the function is an lvalue reference to non-const, and such references cannot be bound to rvalues1.

if a regular constant can be passed like this:

In that example, you have an lvalue reference to const. Lvalue references to const can be bound to rvalues. In the case of test(a), the type of the referred object even matches with the type of the reference, so there isn't even a conversion and parameter refers directly to the passed argument.

Why isn't it doing the same in the case of passing a pointer by reference?

See 1 above

Why do I have to specify const above before int *b for it to compile?

Because the parameter is an lvalue reference to non-const pointer to const. If you don't specify const there, then you have a pointer to non-const, and thus you would need a conversion such as in the case of &a and thus the result would be an rvalue and thus the lvalue reference to non-const cannot be bound.

You could make the pointer function analogous to the integer example by using a reference to const that matches with the type of the argument:

int* a;

// analogous to your int example:
void test1(int* const& in);
test1(&a); // OK, no conversion

// now, we have a reference to non-const,
// but that's fine since there is no conversion:
void test2(int* const& in);
test2(&a); // OK, no conversion

// now there is conversion, but that's fine since
// it is an lvalue reference to const:
void test3(const int* const& in); 
test3(&a); // OK, conversion

// there's a conversion here and the reference isn't
// const but that's fine since it's an rvalue reference:
void test4(const int*&& in); 
test4(&a); // OK, conversion

Or, you could make the integer example fail by making it analogous to the pointer example by introducing a conversion and by using a reference to non-const:

long b;

// analogous to your pointer example:
void test5(int& in);
test5(b); // NOT OK

Note that whether the referred type is a pointer to const or pointer to non-const is entirely separate from the reference being a reference to const or non-const.

eerorika
  • 232,697
  • 12
  • 197
  • 326
0

(you already have an answer, but I wanted to phrase it differently)

You just fell victim to the left const (aka. west const)!

I assume you know by now that you can make a const-lref to a temporary value, but not a regular lref. This is just the language protecting you from easy typos that would be a pain to find.

void test(const int*& in)

This function takes an lvalue reference to a nonconst pointer to a const integer. That means test(&a); would be attempting to convert a temporary pointer (to const int) to a lvalue reference to a pointer (to const int), which is illegal. It is the equivalent of:

void test(int& in) {}
test(5);

If you want to pass a temporary pointer to the function, you have to declare the (reference to) the pointer itself to be constant.

void test(const int*const & in)

Now that you know this, the answer to your second question should be simple as well. Imagine what could happen if you passed a pointer-to-nonconst to your function.

const int globalInt = 0; // This variable cannot be modified

void test(const int*& in) {
    in = &globalInt; // This is possible since the pointer will point to a const int... right?
}

int main() {
    int* ptr; // Pointer to a nonconst int
    test(ptr); // If this function could take this argument...
    
    // Later...

    *ptr = 5; // This is legal because the pointer is nonconst...
              // But it somehow carries the address of globalInt. Oh no.
}
IWonderWhatThisAPIDoes
  • 1,000
  • 1
  • 4
  • 14
  • Seems like the compiler behaviour gets specific when `references` and `pointers` are mixed, done to avoid issues like you explained. – Dan May 27 '21 at 17:30