3

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.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
Mark H
  • 585
  • 3
  • 11
  • 4
    `a` is not a reference, it's a simple `long`. – LogicStuff Dec 14 '16 at 09:56
  • 3
    ^^^^^^ [Yup, it's just `long`](http://ideone.com/lGpcWt) – StoryTeller - Unslander Monica Dec 14 '16 at 09:57
  • 2
    If you don't understand why it's long, the reason is type promotion. And you should [read the answer to this](http://stackoverflow.com/questions/6770258/how-do-promotion-rules-work-when-the-signedness-on-either-side-of-a-binary-opera) – StoryTeller - Unslander Monica Dec 14 '16 at 09:59
  • I have to agree that the error message is misleading if not wrong. It should read `invalid initialization of non-const reference of type ‘int&’ from an rvalue of type ‘long’`. VS2015's error message is more helpful: `'void foo(int &)': cannot convert argument 1 from 'long' to 'int &'` – Simon Kraemer Dec 14 '16 at 10:00
  • @LogicStuff Updated question, it wasn't the type, but the rvalue error that confused me. – Mark H Dec 14 '16 at 10:00
  • @StoryTeller I corrected the question: I meant the rvalue part of the error. – Mark H Dec 14 '16 at 10:02
  • 2
    The error makes perfect sense when you consider `x` is long. To pass it to a function expecting an `int` (or `int&`), a temporary (rvalue) `int` is created. And it cannot bind to non-const reference. – StoryTeller - Unslander Monica Dec 14 '16 at 10:03
  • @StoryTeller I know where the message comes from yet I still find it unfortunate. It would be better if the compiler had used the type before the implicit cast to explain what does not work...just saw that my proposal was wrong as I wrote `rvalue of type ‘long’` which should have been `lvalue of type ‘long’`. – Simon Kraemer Dec 14 '16 at 11:06

2 Answers2

4

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&).

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
  • I see. I never considered the type conversion creating a temporary value (the rvalue in the error message). This makes perfect sense. Thanks. – Mark H Dec 14 '16 at 10:18
1

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

Bathsheba
  • 231,907
  • 34
  • 361
  • 483