问题
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.
回答1:
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 thedouble
passed so a temporary was generated to hold anint
initialized byss
'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.
回答2:
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.
来源:https://stackoverflow.com/questions/4704404/implicit-conversion-const-reference-vs-non-const-reference-vs-non-reference