C++11 auto type deduction from arithmetic operation on int& and long

337 views Asked by At

I must be misunderstanding how auto c++11 and later resolves types. I have the following code:

void foo(int& x)
{
    ++x;
}

int main()
{
    int i = 2;
    int& ir = i;
    long L = 5;
    auto a = ir/L;
    foo(a);

    return 0;
}

This results in the compiler error:

test.cpp: In function 'int main()':

test.cpp:12:10: error: invalid initialization of non-const reference
of type ‘int&’ from an rvalue of type ‘int’
     foo(a);
          ^

test.cpp:1:6: note:   initializing argument 1 of ‘void foo(int&)’
 void foo(int& x)
      ^~~

However, replacing auto with int (int a = ir/L;) compiles fine and gives the expected result (a == 0 before the call to foo(), and a == 1 after). After playing around with the code and seeing various error messages, I think auto is deduced to long int&. Defining the functions void bar(int x) and void bar(const int& x) results in the error message: call of overloaded ‘bar(long int&)’ is ambiguous.

Correction from comments:

I don't understand how auto x = [int&]/[int] result in an lvalue that can be passed by non-const ref while auto x = [int&]/[long] results in an rvalue that cannot.

2

There are 2 answers

1
leslie.yao On BEST ANSWER

The result of ir/L is long. For arithmetic operator, when binary operators have different types, the result produced will be of the common type; between int and long it would be long.

So auto a = ir/L;, the type of a is long. It can't be passed to foo(int&) because you can't bind lvalue reference to non-const with different type.

On the other hand, given the type of L is int, then for auto a = ir/L;, the type of a will be int, then everything is fine.

About the "the rvalue part of the error", when you pass a long to foo(int&), firstly the compiler will try to convert it to int, which is a temporary (i.e. an rvalue) and can't be bound to lvalue reference to non-const.

long could be implicitly converted to int, and temporary could be bound to lvalue reference to const, so passing a long variable to both bar(int x) and bar(const int&) is fine.

BTW: When you write int a = ir/L;, the result of type long is implicitly converted to int. So you'll get an int then it's fine to pass it to foo(int&).

2
Bathsheba On

The fact that you've used auto is not relevant.

a is a long type, due to the rules of argument promotion.

Because foo takes the parameter by reference, compilation fails since an int& cannot bind to a long type (even if they are the same size and have the same complement representation).