在下面的例子中,struct
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 都接受代码并在
B::operator()
中调用 static_assert
。演示:https://gcc.godbolt.org/z/x6x3aWzoq
哪个编译器就在这里?
GCC(和 Clang)在这种情况下是正确的。
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
转换为不同的类型)。因此,在像 s()
这样的函数调用表达式的重载决策期间,为每个转换函数 ([over.call.object]/2) 引入了代理调用函数。从B
的转换函数中引入的是唯一可行的候选者,因此通过重载决议选择它,并通过先将s
转换为函数指针并调用它来执行调用。
您可以通过在禁用优化的情况下实际编译
s();
调用来看到这一点;将发出对转换函数的调用。
IIRC MSVC 的 lambda 有 多个 转换函数到所有不同调用约定的函数指针,这使得重载决议不明确。