Reputation: 1706
I'm following some C++ tutorials, and frustratingly, the source material states this particular function call cannot be made, but doesn't at all explain why;
template<typename T>
void Swap(T &a, T &b){
T temp;
temp = a;
a = b;
b = temp;
}
So now I create an explicit template function instantiation and pass in a
and b
;
int a = 5;
double b = 10.3;
Swap<double>(a, b);
Which then throws the following compiler error;
cannot convert 'a' (type 'int') to type 'double&'
My only hunch is that this is because of the rule that states an rvalue can't be bound to a non-const reference, however, if T &a
is just an alias, it's not an rvalue itself, is it? My implicit cast is creating a temporary, which it can't bind to T &a
? Is that what's happening?
temp
is an lvalue. It may only have function scope, but it does exist. So why wouldn't it allow the implicit cast to double, then assign the reference a to temp
? The intention looks pretty clear. I don't quite understand references as parameters. If a
is getting assigned to temp, which is an lvalue, then wouldn't this be allowed?
That's assuming I'm even on the right track here.
Edit:
A second example;
int a = 5;
double &d = a; // invalid initialization of reference of type 'double&' from expression of type 'int'
However;
int a = 5;
const double &d = a; //valid
Upvotes: 3
Views: 2309
Reputation: 58352
Intuitively, swapping a
and b
should work, because the compiler can convert between int
and double
. In practice, though, think about what the compiler has to do in order to do what you're asking.
You start with a template:
template<typename T>
void Swap(T &a, T &b){
T temp = a;
a = b;
b = temp;
}
To instantiate it for double
, the compiler creates a function something like this:
void Swap(double& a, double& b) {
double temp = a;
a = b;
b = temp;
}
Those are reference parameters - they point to actual locations in memory, rather than being aliases or copies. Under the hood, a reference behaves like a pointer, so you can think of your function similarly to this, if it helps:
void Swap(double* a, double *b);
In other words, Swap
needs references (pointers) to two in-memory doubles - two 8-byte sections of memory (assuming a double takes 8 bytes). As intelligent and intuitive humans, we know that Swap
's implementation doesn't really need two 8-byte sections of memory, but that's how it's declared, and so that's how the compiler enforces it.
To make a version of Swap that can handle mixed arguments, you'd have to do something like this:
template<typename T1, typename T2>
void Swap(T1& a, T2& b) {
T1 tmp(a);
a = b;
b = tmp;
}
Upvotes: 5
Reputation: 302748
What you're trying to do is effectively:
int a = 5;
double& dr = a;
The rules that govern reference initialization in the standard are in §8.5.3. First, some definitions:
(4) Given types “cv1
T1
” and “cv2T2
,” “cv1T1
” is reference-related to “cv2T2
” ifT1
is the same type asT2
, orT1
is a base class ofT2
. “cv1T1
” is reference-compatible with “cv2T2
” ifT1
is reference-related toT2
and cv1 is the same cv-qualification as, or greater cv-qualification than, cv2.
Then, we have:
(5) A reference to type “cv1
T1
” is initialized by an expression of type “cv2T2
” 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 “cv2T2
,”- ..
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
— Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be
const
), or the reference shall be an rvalue reference.
The "then" case does not apply, since int
is not reference-compatible with double
. So we go to the other case, which states that the result should be an rvalue reference. That is, the reference shall be either:
const double& dcr = a; // lvalue reference to a non-volatile const type
double&& drr = a; // rvalue reference
But that is not what your code is doing - hence the error. In fact, what you want to do is explicitly mentioned in the standard as a counter-example in the same bullet point:
int i = 2;
double& rd3 = i; // error: type mismatch and reference not const
Upvotes: 3
Reputation: 57678
The compiler is looking for a Swap(int&, double&)
, because you supplied an int
as the first parameter and a double
as the second and it can't find one.
You could perform the following:
int a = 5;
double b = 10.3;
double c = a * 1.0;
Swap<double>(a, c);
Essentially, you declared a function that wants references to two double
s. When the compiler converts an int
to a double
, it creates a temporary. You function doesn't want a temporary, because it may modify the value (which is what the reference means).
Also, does it make sense to swap an integer with a floating point?
Upvotes: 0