GCC vs clang -使用' make_overload '可变lambda继承时的模糊重载
gcc vs clang - ambiguous overload when using `make_overload` variadic lambda inheritance
又是一轮clang vs gcc的时间了。godbolt.org上的实例。
测试0:重载可调用对象
struct Trad
{
auto operator()(int) { return 1; }
auto operator()(float) { return 2; }
auto operator()(double) { return 3; }
};
int main()
{
assert(Trad{}(1) == 1);
assert(Trad{}(1.f) == 2);
assert(Trad{}(1.0) == 3);
}
- g++ 5.2编译并运行。
- clang++ 3.5 (及以后的版本)编译并运行。
Test 1:重载可调用对象,通过lambda继承生成
template <typename... TFs>
struct overload_set : TFs...
{
overload_set(TFs... fs) : TFs(fs)... {}
};
template <typename... TFs>
auto overload(TFs&&... fs)
{
return overload_set<TFs...>{fs...};
}
int main()
{
auto func = overload
(
[](int) { return 1; },
[](float) { return 2; },
[](double) { return 3; }
);
assert(func(1) == 1);
assert(func(1.f) == 2);
assert(func(1.0) == 3);
}
g++ 5.2无法编译。
错误:请求成员'operator()'是不明确的
clang++ 3.5 (及以后的版本)编译并运行。
这里哪个编译器是正确的?
我可以给你一个变通办法。
template <typename... TFs>
struct overload_set : TFs...
{
overload_set(TFs... fs) : TFs(fs)... {}
};
在这里,我们继承了一堆不同的父类型,每个父类型都有一个operator()
。它们不会(至少在gcc中)以您想要的方式重载。
为了解决这个问题,我们线性继承并通过using
将()
向下携带:
template<class...Ts>
struct inherit_linearly;
template<>
struct inherit_linearly<>{};
template<class T0, class...Ts>
struct inherit_linearly<T0, Ts...>:
T0, inherit_linearly<Ts...>
{
using T0::operator();
using inherit_linearly<Ts...>::operator();
template<class A0, class...As>
inherit_linearly( A0&&a0, As&&...as ):
T0(std::forward<A0>(a0)),
inherit_linearly<Ts>(std::forward<As>(as)...)
{}
};
现在我们替换overload_set
如下:
template <typename... TFs>
struct overload_set : inherit_linearly<TFs...>
{
using inherit_linearly<TFs...>::operator();
overload_set(TFs... fs) :
inherit_linearly<TFs...>(std::forward<TFs>(fs)...)
{}
};
和GCC和clang应该都喜欢它。
线性继承是次优的:平衡二叉树更好,但需要更多的工作。(基本上,你拿一个Xs...
包,然后用TMP把它分成Xs_front...
和Xs_back...
,把它们放在types<...>
包里,把它们转录给你的父母,然后做using blah::operator()
的事情)。这是因为编译器对递归模板实例化和继承深度的限制往往比总模板实例化和继承"量"的限制更浅。
在c++17中,我们不需要做这种线性继承:
template <typename... TFs>
struct overload_set : TFs...
{
using TFs::operator()...;
overload_set(TFs... fs) : TFs(fs)... {}
};
因为他们增加了一个新的位置你可以做...
扩展
相关文章:
- 继承函数的重载解析
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 继承期间显示未知行为的子类
- 可组合的lambda/std::函数与std::可选
- 头文件-继承c++
- 为什么在保护模式下继承升级不起作用
- C++Boost Asio Pool线程,带有lambda函数和传递引用变量
- 通过继承类使用来自不同命名空间的运算符
- 子目录是否继承属性,例如add_definitions,include_directories和父Cmakelist.t
- 如何建立使用模板函数的lambda函数的尾部返回类型
- 混合组合和继承的C++问题
- 继承:构造函数,初始化C++11中基类的类C数组成员
- 在 lambda 函数 g++-4.8 中调用继承的受保护子类型
- 继承类中没有匹配的泛型委托作为 lambda 参数
- 与lambda一起使用虚拟继承在初始化列表中捕获此问题的GCC错误
- 通用lambda,继承和尾随返回类型:此有效代码
- 可以用参数包编译C 17 lambda继承的编译器
- 从lambda继承是什么意思
- GCC vs clang -使用' make_overload '可变lambda继承时的模糊重载
- 我还需要使用可变模板继承来创建lambda重载