在下面的示例中,结构体S
继承了两个函数对象A
和B
,每个对象都有自己的operator ()
,然后声明using A::operator()
从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?
}
如我所料,这个代码被MSVC拒绝,错误如下:
error C2064: term does not evaluate to a function taking 0 arguments
因为A::operator(int)
确实接受1个参数,所以B::operator()
不被考虑。
但是GCC和Clang都接受这个代码并在static_assert
中调用B::operator()
。演示:https://gcc.godbolt.org/z/x6x3aWzoq
哪个编译器在这里?
GCC(和Clang)在这种情况下是正确的。
一个无捕获的非泛型lambda有一个到函数指针的转换函数([expr.prim.lambda.closure]/8),它被S
继承(并且不冲突,因为A
和B
的转换函数转换为不同的类型)。因此,在像s()
这样的函数调用表达式的重载解析期间,为每个转换函数([over.call.object]/2)引入代理调用函数。从B
的转换函数引入的指针是唯一可行的候选者,因此通过重载解析选择它,并通过首先将s
转换为函数指针并调用它来执行调用。
你可以通过编译一个禁用优化的s();
调用看到这一点;将触发对转换函数的调用。
IIRC MSVC的lambdas有多个转换函数到所有不同调用约定的函数指针,这使得重载解析有歧义。