简体   繁体   中英

Const and non-const reference binding

void swap(int& a, int& b) {}

void copy(int& a, const int& b) {}

int main() {
   int a=1;
   unsigned b=2;

   swap(a, b);
   copy(a, b);
}

C++ language, the g++ compiler.

Please tell me why there won't be a compilation error in a copy -function call, but in a swap -function triggers invalid initialization of reference of type 'int&' from expression of type 'unsigned int' .

Sorry for my bad English.

Short story

First of all - the rules L for reference binding of an expression of type U with qualification cv2 to a reference of type T with qualification cv1 .:

A reference to cv1 T can be initialized by an expression of type cv2 U

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

    • is an lvalue and cv1 T is reference-compatible with cv2 U , or
    • has a class type [ ... ].
  • Otherwise, cv1 shall be const or the reference shall be an rvalue reference.

cv1 T is reference-compatible to cv2 U if T is the same types as U (or a base of U ) and if cv1 is equal to cv2 (or greater).

Unfortunatelly (or luckily?! ;)) a function with a non-constant lvalue reference parameter cannot be called with an lvalue of a non-reference-compatible type (or in case of class types without a viable conversion of the passed argument to a reference-compatible type).

In detail

Let's consider a function that takes an integer reference and a second one that has a constant integer reference paramter.

void doSomething (int & x)
{
  // do some READ & WRITE stuff with x
  x = x+5;
}

int doSomethingElse (int const & x)
{
  // do some READ ONLY stuffwith x
  return 3*x;
}

Let's look at a single signed and another unsigned value:

 int a = 1;
 unsigned int b = 2;

Now we pass the int named a to doSomething() :

 // works since x of doSomething can bind to a
 doSomething(a);

 // let's try to expand/"inline" what is happening
 {
   int & x = a;
   x = 5;
 }

No magic here, reference x binds to a and is set to 5 (and therefore a, too). Fine.

Now we try to pass b to the same function. But ...

   // ... it doesn't work/compile since
   // unsigned int is not reference compatible to int
   doSomething(b); // compile error here

   // what's going on here
   {
     int & x = b; // unsigned value cannot bind to a non-const lvalue reference!
                  // compile error here
     x = 5;
   }

Here we start having trouble and calling doSomething with b will not compile.

Let's look at the const reference function now. Passing a is obviously not problematic again. A const int reference is bound to an int value.

int c = doSomethingElse(a);

// let's do some naive inlining again
int c;
{
  int const & x = a;
  c = 3*x;
}    

Well seems alright. c will be 3*a .

Now what happens if we pass b to that function? The standard says that, in this case, a temporary of type cv1 T is created and initialized from the initializer expression using copy initialization rules.

int d = doSomethingElse(b);

// expanding this to:
int d;
{
  int temp = b; // implicit conversion, not really an lvalue!
  int const & x = temp;
  d = 3*x;
} 

For swap second param wants int& and you pass b that is unsigned. It's not good as int& and no help available.

Copy needs const int&, what allows using a temporary. You pass it unsigned, that can be implicitly converted to int, and that int temporary can be passed to copy.

The big difference is that the second case will not modify the second argument, thus it can be replaced under the hood. If temporary was allowed for the swap case you'd swap a with the temporary and leave b unchanged that is hardly what anyone wants.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM