【问题标题】:avoiding ambiguous SFINAE access when both base and derived class have variadic inheritance chains当基类和派生类都具有可变继承链时,避免模棱两可的 SFINAE 访问
【发布时间】:2014-06-01 08:31:51
【问题描述】:

使用 SFINAE,可以访问可变参数类模板的各个元素。当基类从可变参数类模板继承,然后派生类从基类和可变参数类模板(使用不同的模板参数)继承时,就会出现我的问题。关于应该遵循哪个继承链存在歧义。有没有办法解决歧义?

例如:

// compile with flag: -std=c++11
#include <type_traits>

struct A { int x; };
struct B { int x; };
struct C { int x; };
struct D { int x; };

template <class ... Params> class Parameter { };

template <class Param, class ... Tail>
class Parameter<Param, Tail ...> : public Param, public Parameter<Tail ...>
{
  public:
    //! Get a parameter
    template <class Param2>
    typename std::enable_if<std::is_same<Param, Param2>::value, int>::type
    getParam() const
    { return Param::x; }

    //! Get a parameter. Delegate false template matches down the Tail... inheritance line (see SFINAE)
    template <class Param2>
    typename std::enable_if<! std::is_same<Param, Param2>::value, int>::type
    getParam() const
    { return Parameter<Tail ...>::template getParam<Param2>(); }
};

class Base : public Parameter<A, B>
{ };

class Derived : public Base, public Parameter<C, D>
{ };

int main(int const argc, char const * argv[])
{
  Base base;
  int a = base.getParam<A>(); // ok
  int b = base.getParam<B>(); // ok

  Derived derived;
  int c0 = derived.getParam<C>(); // error: request for member ‘getParam’ is ambiguous
  int c1 = derived.Derived::getParam<C>();  // error: request for member ‘getParam’ is ambiguous
  int c2 = derived.Parameter<C, D>::getParam<C>(); // ok but syntax overly complex, especially if many params

  int a0 = derived.getParam<A>(); // error: request for member ‘getParam’ is ambiguous
  int a1 = derived.Base::getParam<A>(); // ok, could be acceptable if also worked on Derived
  int a2 = derived.Parameter<A, B>::getParam<A>(); // ok but syntax overly complex and confusing

  return 0;
}

我意识到几个主要的设计更改可以解决这个问题:1)摆脱可变参数类模板,而是从每个参数一次派生一个 2)使参数成为成员变量。但我的问题是我是否可以保留可变参数类模板同时避免歧义。

【问题讨论】:

  • 就个人而言,我会这样写:ideone.com/BbLKxb
  • @Jarod42 这不是评论,这是我认为的答案!
  • @jarod42 在单继承情况下的轻微行为变化:如果我们是Parameter&lt;int,int&gt;,上面得到第一个int,而您的代码是模棱两可的。如果 OP 关心,可以修补,但 OP 可能希望在这种情况下出现错误....
  • 所有给定参数的类型在我们的应用程序中都是不同的,所以没有问题,但请参阅下面的其他评论。
  • 附加说明:理想情况下,我希望 a0 和 c0 之类的东西可以工作,如果没有,类似于 a1 和 c1 的东西就可以了。像 a2 和 c2 这样的东西太麻烦了,会让我完全远离可变参数模板实现。因此,不必通过原始帖子中提到的所有错误情况,只需避免重新列出所有参数参数就足够了。

标签: templates inheritance c++11 variadic-templates sfinae


【解决方案1】:

我会删除不匹配的getParam 以避免歧义,如下所示:Demo

template <class ... Params> class Parameter;

template <class Param>
class Parameter<Param> : public Param
{
public:
    //! Get a parameter
    template <class Param2>
    typename std::enable_if<std::is_same<Param, Param2>::value, int>::type
    getParam() const
    { return Param::x; }
};

template <class Param, class ... Tail>
class Parameter<Param, Tail ...> : public Parameter<Param>, Parameter<Tail...>
{
public:
    using Parameter<Param>::getParam;
    using Parameter<Tail...>::getParam;
};

class Base : public Parameter<A, B> {};

class Derived : public Base, public Parameter<C, D>
{
public:
    using Base::getParam;
    using Parameter<C, D>::getParam;
};

【讨论】:

  • 这很好,但是using Parameter&lt;C, D&gt;::getParam; 让用户写起来很烦人,因为他们需要使该语句与上面的public Parameter&lt;C, D&gt; 保持同步,因为他们正在充实 Derived 并添加更多内容给它的参数。有什么方法可以避免在两个地方提到Parameter&lt;C, D&gt;
  • 如果Derived 不是从Base 派生的,而只是想扩展到CD,您可以在class Parameter 中添加template &lt;typename ... Ts&gt; using extend = Parameter&lt;Param, Ts...&gt;;,然后添加class Derived : public Base::extend&lt;C, D&gt; .
  • 我最终使用了一个空的 Parameter<...> 类,它只是像你一样剥离 Parameter ,然后我只是在那些上调用常规成员函数,以参数名称为前缀(如derived.Parameter&lt;C&gt;::get())。所以我接受你的回答,但我的结论仍然是两个可变参数模板继承链之间的歧义是不可避免的。
猜你喜欢
  • 2022-01-23
  • 1970-01-01
  • 1970-01-01
  • 2012-09-29
  • 1970-01-01
  • 2015-10-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多