如何为特定类型专门化模板 class 方法?

How to specialize a template class method for a specific type?

我有这样的代码:

class Bar {
 public:
  void print() {
    std::cout << "bar\n";
  }
};

template<typename T>
class Foo {
 public:
  template <typename std::enable_if<std::is_base_of<T,Bar>::value,T>::type>
  void print() {
    t.print();
  }

 template <typename>
  void print() {
    std::cout << t << std::endl;
  }
 private:
  T t;
};

int main() {
//  Foo<int> foo1;
  Foo<Bar> foo2;
  foo2.print();
}

这段代码的目的是:如果T tBarBar的子类,则foo.print()推导为void print() {t.print();},否则推导为 void print() {std::cout << t << std::endl;},但事情并没有像我预期的那样工作。编译器错误:

"a non-type template parameter cannot have type 'typename std::enable_if::value, Bar>::type' (aka 'Bar')",

这段代码有什么问题?

  1. 您应该将 print() 重载到函数模板(以使 SFINAE 工作),否则始终首选 non-template 函数。

  2. 你应该让print()有自己的模板类型参数;类型检查不应直接在 class 模板参数 T 上执行,函数模板重载解析和 SFINAE 是在函数模板本身上执行的,class 模板不参与。

  3. 您可以将std::enable_if的部分移动到return类型。

  4. 如果您希望类型为 Bar,则应将指定的顺序更改为 std::is_base_of(即 std::is_base_of<Bar, X>,而不是 std::is_base_of<X, Bar>)或Bar.

  5. 的派生 class

例如

template <typename X = T>
typename std::enable_if<std::is_base_of<Bar, X>::value>::type print() {
  t.print();
}

template <typename X = T>
typename std::enable_if<!std::is_base_of<Bar, X>::value>::type print() {
  std::cout << t << std::endl;
}

LIVE

您应该在调用 print() 时添加模板参数,因为它本身就是一个模板方法。反正你的设计太复杂了!

使用 C++17 变得非常简单,您只需要 一个 Foo<T>::print() 方法。

void print() {
  if constexpr(std::is_base_of_v<T,Bar>) // ignores `Foo<int>`
    t.print();
}

Demo.

由于您实际上对类型是否具有成员函数 print 或定义了 operator<< 感兴趣,因此您也应该以这种方式对其进行约束。

随着即将推出的 C++20 标准,我们得到 concepts & constraints。考虑到这一点,我们可以执行以下操作:

namespace traits
{
template<typename T>
concept has_print_v = requires(T&& t) { t.print(); };

template<typename T>
concept has_ostream_op_v = requires(T&& t, std::ostream& os) { os << t; };
} // end of namespace traits

并使用这样的概念:

template<typename T>
class Foo
{
public:
    void print()
    {
        if constexpr (traits::has_print_v<T>) { t.print(); }
        else if constexpr (traits::has_ostream_op_v<T>) { std::cout << t << "\n"; }
    }
private:
    T t;  
};

LIVE DEMO