0

Recently I have written one function which is doing some processing on double numbers but due to some change I need to do same processing for integers so to cut the long story short with simpler example as given below :

void display(double& arg)
{
    std::cout << arg << std::endl;
}
int main()
{
    int inum = 10;
    display(inum);
    return 0;
}

I found that above code is not compiling and giving error "reference of type "double &" (not const-qualified) cannot be initialized with a value of type "int", since the error is quite intuitive so i modified the code and changed the argument of display function to const double& arg and everything seems to be working correctly.So my question is

What exactly is happening in the "const double&" case? A full explanation is desired?

Now after reading some of the explanation and question link that is provided i understood that conversion produces rvalue and rvalue cannot be bound to temporaries so the only reasoning i m looking for is why conversion produces rvalue ?

PapaDiHatti
  • 1,841
  • 19
  • 26
  • 2
    Making it const allows the compiler to construct a temporary for the argument. – user207421 May 28 '16 at 00:50
  • 1
    I believe temporaries are rvalues but here inum is lvalue so compiler converts lvalue to temporary also ? – PapaDiHatti May 28 '16 at 00:52
  • 2
    Possible duplicate of [IUnknown pointer reference](http://stackoverflow.com/questions/19551510/iunknown-pointer-reference) – Vtik May 28 '16 at 00:56
  • @Kapil The compiler needs an lvalue that is a double. Passing a reference imples that the callee can modify the value, and it doesn't make sense to pass a temporary as a reference unless it's const. Otherwise the callee could do the modification and see no effect, which would just baffle you. – user207421 May 28 '16 at 01:28

3 Answers3

3

Here's an example of the error I see: error: invalid initialization of non-const reference of type 'double&' from an rvalue of type 'double'

It's clearly complaining about double& (which is the parameter type of the display function) cannot be initialized with an rvalue of type double. Well, inum was passed to the function but it's type is int. However, int can be implicitly converted to double and this is happening. The conversion produces an rvalue (i.e., temporary) double but a temporary cannot be bound to a non-const reference.


Changing it to void display(const double& arg) works because everything works the same as explained above. However, an rvalue can be bound to a const reference.


I don't have the official standard but the following quotes are from the n4296 draft:

4 Standard conversions [conv]

1 Standard conversions are implicit conversions with built-in meaning. Clause 4 enumerates the full set of such conversions. A standard conversion sequence is a sequence of standard conversions in the following order:

(1.1) — Zero or one conversion from the following set: lvalue-to-rvalue conversion, array-to-pointer conversion, and function-to-pointer conversion.

3 An expression e can be implicitly converted to a type T if and only if the declaration T t=e; is well-formed, for some invented temporary variable t (8.5).

6 The effect of any implicit conversion is the same as performing the corresponding declaration and initialization and then using the temporary variable as the result of the conversion.

James Adkison
  • 9,412
  • 2
  • 29
  • 43
  • Is there any way by which it can be identified that conversion produces rvalue ? – PapaDiHatti May 28 '16 at 00:58
  • I don't follow your question. Are you asking about a reference to the standard about implicit conversions? – James Adkison May 28 '16 at 01:01
  • Yes that will be helpful as i could not understand the reasoning that conversion produces rvalue – PapaDiHatti May 28 '16 at 01:02
  • @Kapil I don't have an official copy but I'll look through the draft I have. However, the conversion certainly cannot produce an lvalue (i.e., something you can take the address of). – James Adkison May 28 '16 at 01:05
2

double& is a reference to a double, which exists somewhere else. Usually you would use this to modify a variable which the caller provides. But, if the caller provides the address of an int, and you try to treat it like a double, things will go badly for you! The compiler also forbids calling a function like void bar(double&) with a temporary, like bar(3.14), because trying to modify a temporary value usually doesn't make sense.

On the other hand, with const double&, you promise not to change the value: you are only taking it by reference in order to avoid a copy, not to modify it. Therefore, it's okay to call void foo(const double&) with a temporary, as in foo(3.14): the compiler passes along the address of the temporary, foo dereferences it to get the value, and after foo returns the temporary disappears.

So that's what's happening here: when the signature calls for a const double&, and you provide an int, a temporary double is created (cast from the int), and its address is passed into display.

Nick Matteo
  • 4,453
  • 1
  • 24
  • 35
1
  • If inum was a double, your code will work because, your parameter is a lvalue (you can take its address).

  • If your function signature was void display(const double& arg) it will work, because your promoted parameter is a temporary and, you can bind a const reference to a temporary which extends its life time to the scope of the const reference identifier.

  • If your function signature was void display(double&& arg) it will work, because your promoted parameter is a temporary and, you can bind a rvalue reference to a temporary which extends its life time to the scope of the rvalue reference identifier.

  • The difference between the previous two methods above, is that you can modify arg in the latter, and its C++11 and above....

In C++, You cannot bind a non const reference (specifically lvalue) to a temporary.

What happens is that there is an implicit conversion of int to double

Quoting Bo Personn's answer:

The original case for not allowing references to temporaries was for function parameters. Suppose this was allowed:

void inc(double& x)
{ x += 0.1; }

int i = 0;
inc(i);

Why isn't i changed?

Community
  • 1
  • 1
WhiZTiM
  • 21,207
  • 4
  • 43
  • 68