Martyn Shutt
Martyn Shutt

Reputation: 1706

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

Upvotes: 3

Views: 2309

Answers (3)

Josh Kelley
Josh Kelley

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;
}

Demo.

Upvotes: 5

Barry
Barry

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 “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

Upvotes: 3

Thomas Matthews
Thomas Matthews

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 doubles. 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

Related Questions