2 回答
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 typecv2 U
if the reference is an lvalue reference and the initializer expression
- is an lvalue and
cv1 T
is reference-compatible withcv2 U
, or- has a class type [ ... ].
Otherwise,
cv1
shall beconst
or the reference shall be an rvalue reference.
cv1 T
is reference-compatible tocv2 U
ifT
is the same types asU
(or a base ofU
) and ifcv1
is equal tocv2
(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.