Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit conversion : const reference vs non-const reference vs non-reference

Consider this code,

struct A {};
struct B {  B(const A&) {} };
void f(B)
{
    cout << "f()"<<endl;
}
void g(A &a)
{
    cout << "g()" <<endl;
    f(a); //a is implicitly converted into B.
}
int main()
{
    A a;
    g(a);
}

This compiles fine, runs fine. But if I change f(B) to f(B&), it doesn't compile. If I write f(const B&), it again compiles fine, runs fine. Why is the reason and rationale?

Summary:

void f(B);         //okay
void f(B&);        //error
void f(const B&);  //okay

I would like to hear reasons, rationale and reference(s) from the language specification, for each of these cases. Of course, the function signatures themselves are not incorrect. Rather A implicitly converts into B and const B&, but not into B&, and that causes the compilation error.

like image 857
Nawaz Avatar asked Jan 16 '11 08:01

Nawaz


People also ask

Can a const reference be bound to a non-const object?

No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .

What are the differences between passing a parameter by reference and by constant reference?

The important difference is that when passing by const reference, no new object is created. In the function body, the parameter is effectively an alias for the object passed in. Because the reference is a const reference the function body cannot directly change the value of that object.

What is a const reference?

A const reference is actually a reference to const. A reference is inherently const, so when we say const reference, it is not a reference that can not be changed, rather it's a reference to const. Once a reference is bound to refer to an object, it can not be bound to refer to another object.

Can a const function return a non-const reference?

If the thing you are returning by reference is logically part of your this object, independent of whether it is physically embedded within your this object, then a const method needs to return by const reference or by value, but not by non-const reference.


2 Answers

I would like to hear reasons, rationale and reference(s) from the language specification

Is The Design and Evolution of C++ sufficient?

I made one serious mistake, though, by allowing a non-const reference to be initialized by a non-lvalue [comment by me: that wording is imprecise!]. For example:

void incr(int& rr) { ++rr; }

void g()
{
    double ss = 1;
    incr(ss);    // note: double passed, int expected
                 // (fixed: error in release 2.0)
}

Because of the difference in type the int& cannot refer to the double passed so a temporary was generated to hold an int initialized by ss's value. Thus, incr() modified the temporary, and the result wasn't reflected back to the calling function [emphasis mine].

Think about it: The whole point of call-by-reference is that the client passes things that are changed by the function, and after the function returns, the client must be able to observe the changes.

like image 177
fredoverflow Avatar answered Sep 21 '22 13:09

fredoverflow


The problem is that the implicit conversion from a to a B object yields an rvalue. Non-const references can only bind to lvalues.

If B had a default constructor you would get the same behavior if you change the f(a) call to f(B()).

--

litb provides a great answer to what is an lvalue: Stack Overflow - often used seldom defined terms: lvalue

GotW #88: A Candidate For the “Most Important const”

Stack Overflow - How come a non-const reference cannot bind to a temporary object?

--

To explain with references to the standard how those function calls fail or succeed would be excessively long. The important thing is how B& b = a; fails while const B& b = a; does not fail.

(from draft n1905)

A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as follows:
- [is an lvalue and is either reference compatible or implicitly convertible to an lvalue of a reference compatible type...]
- Otherwise, the reference shall be to a non-volatile const type (i.e., cv1 shall be const).

Here's a case where something is convertible to an lvalue of reference compatible type.

like image 39
Chris Hopman Avatar answered Sep 19 '22 13:09

Chris Hopman