Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

cannot convert 'a' (type 'int') to type 'double&

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
like image 641
Martyn Shutt Avatar asked Dec 29 '25 08:12

Martyn Shutt


2 Answers

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.

like image 91
Josh Kelley Avatar answered Dec 31 '25 23:12

Josh Kelley


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 “cv2 T2,” “cv1 T1” is reference-related to “cv2 T2” if T1 is the same type as T2, or T1 is a base class of T2. “cv1 T1” is reference-compatible with “cv2 T2” if T1 is reference-related to T2 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 “cv2 T2” 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 “cv2 T2,”
    • ..

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
like image 28
Barry Avatar answered Dec 31 '25 22:12

Barry