通过引入不相关的调用运算符-clangvsgcc奇怪地解决了模棱两可的调用运算符重载
考虑实现一个overload_two类的任务,以便编译以下代码:
int main()
{
overload_two o{
[](int) { return 1; },
[](char){ return 2; }
};
static_assert(o(0) + o('a') == 3);
}
第一次尝试看起来像这样:
template <typename F, typename G>
struct overload_two : F, G
{
overload_two(F f, G g) : F{f}, G{g} { }
};
当然,这会失败并出现以下错误(gcc 11.0.1):
<source>:15:22: error: request for member 'operator()' is ambiguous 15 | static_assert(o(0) + o('a') == 3); | ^ <source>:12:9: note: candidates are: 'main()::<lambda(char)>' 12 | [](char){ return 2; } | ^ <source>:11:9: note: 'main()::<lambda(int)>' 11 | [](int) { return 1; }, | ^
预期的解决方案是将F::operator()和 都G::operator()带入using声明的范围。然而——非常令人惊讶的是——引入一个看似无关的方法operator()也有效:
template <typename F, typename G>
struct overload_two : F, G
{
overload_two(F f, G g) : F{f}, G{g} { }
auto operator()(F&) { } // wtf?
};
Godbolt.org 上的现场示例
更重要的是,clang 13.0.0 似乎在没有using声明或 random 的情况下工作operator():godbolt.org 上的现场示例。
我的问题是:
-
gcc拒绝执行
overload_two没有任何operator()或using声明是否正确? -
gcc 接受
overload_twowithoperator()(F&)和 nousing声明的实现是否正确? -
clang 在
overload_two没有任何operator()或using声明的情况下接受实施是否正确?
回答
- 是的。名称的名称查找
operator()是不明确的,因为它在两个碱基中都找到了名称(请参阅[class.member.lookup]/5.2)。那是一个错误,不要通过GO,不要收集200美元。 - 是的。无关的
operator()通过隐藏基类成员使名称查找成功。因为您的 lambda 是无捕获的,所以它们可以隐式转换为函数指针,并且这些隐式转换由派生类继承。当您的类可转换为函数指针(或引用)时,重载解析将额外从这些转换生成代理调用函数。这些将被选择用于示例中的调用。 - 不。见#1。
THE END
二维码