与 C++ 中的“using”子句相反的继承运算符的选择

Selection of inherited operator contrary to `using` clause in C++

在下面的示例中,struct S 继承自两个功能对象 AB,每个对象都有自己的 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), 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)。从B的转换函数中引入的是唯一可行的候选者,因此通过重载决议选择它,并通过先将s转换为函数指针并调用它来执行调用。

您可以通过实际编译禁用优化的 s(); 调用来看到这一点; a 将发出对转换函数的调用。


IIRC MSVC 的 lambda 有多个 转换函数到所有不同调用约定的函数指针,这使得重载解析不明确。