Question

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.

Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top