使用std :: enable_if和variadic基类

时间:2014-09-10 06:23:29

标签: c++ templates c++11 enable-if

以下是我正在尝试做的一个简要示例:

#include <string>
#include <iostream>
#include <type_traits>

template <typename T>
class foo
{
public:
    template <typename U>
    typename std::enable_if<std::is_same<T, U>::value>::type
    bar(const U& t)
    {
        std::cout << t << "\n";
    }
};

template <typename... Args>
class baz
  : public foo<Args>...
{

};

int main()
{
    baz<double, std::string> b;
    b.bar(1.0);
}

这给了我模糊的功能错误:

error: request for member 'bar' is ambiguous

b.bar(1.0);

note: candidates are: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = std::basic_string<char>]

note: template<class U> typename std::enable_if<std::is_same<T, U>::value>::type foo<T>::bar(const U&) [with U = U; T = double]

我的问题有两个:

  1. 为什么内部模板U没有推断出来?我认为这是由于模板扣除和重载解析的排序,但有人可以解释一下吗?
  2. 还有另一种方法可以解决我想要做的事情吗?

2 个答案:

答案 0 :(得分:5)

我认为错误信息具有误导性。问题实际上名称bar在多个基类中可用,并且您使用using指令将您想要的名称带入派生类范围。

这是一个有效的解决方案:

template <typename X, typename... Args>
class baz : public foo<X>, public baz<Args...>
{
    public:
        using foo<X>::bar;        //bring the name from the first base
        using baz<Args...>::bar;  //bring the name from the second base
};

template <typename X>
class baz<X> : public foo<X>   //specialization for one argument
{
        //no using directive needed, as there is one base only!
};

Complete Demo

答案 1 :(得分:4)

该问题与可变参数模板,模板参数推导等无关。这是来自不同基类的同名成员函数不会重载。最小化的例子:

struct foo {
  void f(int &);
};

struct bar {
  void f(const int &);
};

struct foobar : foo, bar { };

int main(){
  foobar fb;
  int i;
  fb.f(i); // clang complains: error: member 'f' found in multiple base classes of different types
}

由于在您的代码中,foo<double>foo<std::string>是不同的类型,而bar的查找在每个代码中都找到了声明,因此您的代码格式不正确。


可能的解决方法是编写一个明确分派给相应baz::bar的{​​{1}}:

foo::bar

如果需要,您可以template <typename... Args> class baz : public foo<Args>... { public: template <typename U> void bar(const U& t) { foo<U>::bar(t); } }; baz::bar上的SFINAE U作为Args中的某种类型。

另一种可能的解决方案是使用Nawaz's answer中显示的递归实现。