gcc vs clang - 使用 `make_overload` 可变参数 lambda 继承时出现不明确的重载 [重复]

2024-01-26

又一轮的时间到了铿锵与海湾合作委员会. godbolt.org 上的实例 https://goo.gl/RDPCKD.


Test 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 编译并运行。
  • 铿锵++ 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 无法编译。

    • error: request for member 'operator()' is ambiguous

  • 铿锵++ 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...你把它分成Xs_front... and Xs_back...使用小心的 TMP,将它们放入types<...>打包,将它们转录给你的两个父母,然后做using blah::operator()事物)。这是因为编译器对递归模板实例化和继承深度有限制,该限制往往比总模板实例化和继承“量”的限制更浅。


In c++17 /questions/tagged/c%2b%2b17我们不必进行这种线性继承:

template <typename... TFs>
struct overload_set : TFs...
{
  using TFs::operator()...;
  overload_set(TFs... fs) : TFs(fs)... {}
};

因为他们增加了一个你可以做的新地点...扩张。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

gcc vs clang - 使用 `make_overload` 可变参数 lambda 继承时出现不明确的重载 [重复] 的相关文章

随机推荐