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
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;
}
Demo.
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” ifT1is the same type asT2, orT1is a base class ofT2. “cv1T1” is reference-compatible with “cv2T2” ifT1is reference-related toT2and 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
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With