I tested the following code with GCC, Clang, ICC and VS:
void f() {}
void g(void (&&)()) { }
int main() {
g(f);
}
As we can see, g
takes an rvalue reference but f
is an lvalue and, in general, rvalue references cannot be bound to lvalues. That's exactly what ICC complains about:
error: an rvalue reference cannot be bound to an lvalue
VS also gives an error but for another reason:
error C2664: 'void h(void (__cdecl &&)(void))' : cannot convert parameter 1 from 'void (__cdecl *)(void)' to 'void (__cdecl &&)(void)'
This suggests to me that VS is immediately performing a function-to-pointer conversion rather than directly bind the reference to f
. It's worth mentioning that if I replace g(f)
with g(&f)
then the four compilers yield this very same error.
Finally, GCC and Clang accept the code and I believe they are correct. My reasoning is based on 8.5.3/5
A reference to type “cv1 T1” is initialized by an expression of type “cv2 T2” as
— If the reference is an lvalue reference [...]
— Otherwise, [...] the reference shall be an rvalue reference.
— If the initializer expression is a [...] function lvalue [...]
then the reference is bound to the value of the initializer expression [...]
Is my interpretation correct (that is, Clang and GCC are compliant for the given reason)?
An lvalue reference can bind to an lvalue, but not to an rvalue.
Typically rvalues are temporary objects that exist on the stack as the result of a function call or other operation. Returning a value from a function will turn that value into an rvalue. Once you call return on an object, the name of the object does not exist anymore (it goes out of scope), so it becomes an rvalue.
Rvalue references is a small technical extension to the C++ language. Rvalue references allow programmers to avoid logically unnecessary copying and to provide perfect forwarding functions. They are primarily meant to aid in the design of higer performance and more robust libraries.
So an rvalue can be used both with rvalue overloads and a const lvalue reference.
Is my interpretation correct [...]?
Yes.
Your interpretation is correct because of the Paragraph of the Standard that you quoted. A further confirmation comes from Paragraph 13.3.3.1.4/3 on reference binding:
Except for an implicit object parameter, for which see 13.3.1, a standard conversion sequence cannot be formed if it requires binding an lvalue reference other than a reference to a non-volatile const type to an rvalue or binding an rvalue reference to an lvalue other than a function lvalue. [...]
Paragraph 13.3.3.2/3 contains a further (indirect) confirmation:
[...] Standard conversion sequence S1 is a better conversion sequence than standard conversion sequence S2 if
— [...]
— S1 and S2 are reference bindings (8.5.3) and S1 binds an lvalue reference to a function lvalue and S2 binds an rvalue reference to a function lvalue. [ Example:
int f(void(&)()); // #1 int f(void(&&)()); // #2 void g(); int i1 = f(g); // calls #1
—end example ]
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With