In the following example struct S
inherits from two functional objects A
and B
each with its own operator ()
, and then declares using A::operator()
to take the operator from A
:
using A = decltype([](int){ return 1; });
using B = decltype([](){ return 2; });
struct S : A, B {
using A::operator();
};
int main() {
S s;
static_assert( s() == 2 ); // passes in GCC and Clang, but why?
}
As I expected, this code is rejected by MSVC with the error:
error C2064: term does not evaluate to a function taking 0 arguments
because A::operator(int)
indeed takes 1 argument and B::operator()
shall not be considered.
However both GCC and Clang accept the code and call B::operator()
in static_assert
. Demo: https://gcc.godbolt.org/z/x6x3aWzoq
Which compiler is right here?
GCC (and Clang) are correct in this case.
A captureless nongeneric lambda has a conversion function to function pointer ([expr.prim.lambda.closure]/8), which is inherited by S
(and doesn't conflict since the conversion functions from A
and B
convert to different types). So during overload resolution for a function call expression like s()
, surrogate call functions are introduced for each conversion function ([over.call.object]/2). The one introduced from B
's conversion function is the only viable candidate, so it is selected by overload resolution, and the call is performed by converting s
to a function pointer first and calling that.
You can see this by actually compiling a s();
call with optimization disabled; a the call to the conversion function will be emitted.
IIRC MSVC's lambdas have multiple conversion functions to function pointers for all the different calling conventions, which makes the overload resolution ambiguous.
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