Reputation: 20553
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)?
Upvotes: 12
Views: 821
Reputation: 126582
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 ]
Upvotes: 9