如何使用 enable_if 进行模板 class 成员的外联定义

How to use enable_if for out-of-line definition for a template class member

我正在尝试了解 enable_if 的用法,但我在这方面确实遇到了一些困难。在这里,我编写了一个测试代码,但似乎没有按预期工作。

#include <iostream>

template <typename T>
class Base{
 public:
  template <typename U>
  U Compute(U a, U b);
};

using AddOperation = Base<int>;

template<>
template<typename U>
typename std::enable_if<!std::is_same<U, bool>::value, U>::type
AddOperation::Compute(U a, U b){
  return a + b;
}

int main(){
  Base<int> b;
  std::cout << b.Compute<int>(10, 2) << std::endl;
  std::cout << b.Compute<bool>(true, false) << std::endl;
  return 0;
}

意图:不想为 bool 类型启用计算

但在上面的代码中,它是有效的。如何确保 bool 的 Compute 函数未被编译器专门化?

EDIT1

最终目标是为 T=T1 的 U=bool 启用 Compute 并为 T=T2 的 U=bool 禁用 Compute。这是我试图实现相同目标的另一个示例代码

#include <iostream>

enum class OpType{
  INT,
  BITWISE,
};

template <OpType T>
class Base{
 public:
  template <typename U>
  typename std::enable_if<!std::is_same<U, bool>::value, U>::type 
  Compute(U a, U b); 
};

using AddOperation = Base<OpType::INT>;

template<>
template<typename U>
typename std::enable_if<!std::is_same<U, bool>::value, U>::type
AddOperation::Compute(U a, U b){ 
  std::cout << a << "," << b << std::endl;
  return a + b;
}

using AndOperation = Base<OpType::BITWISE>;

template<>
template<typename U>
typename std::enable_if<std::is_same<U, bool>::value, U>::type
AndOperation::Compute(U a, U b){
  return a & b;
}

int main(){
  AddOperation b;
  AndOperation a;
  std::cout << b.Compute<int>(10, 2) << std::endl;
  std::cout << a.Compute<bool>(true, true) << std::endl;
  return 0;
}

您也应该像定义一样在声明中使用 enable_if

template <typename T>
class Base{
 public:
  template <typename U>
  typename std::enable_if<!std::is_same<U, bool>::value, U>::type Compute(U a, U b);
};

LIVE

事实上,clang 如我所料拒绝了您当前的代码,因为声明和定义不匹配。

error: out-of-line definition of 'Compute' does not match any declaration in 'Base'


编辑(针对您添加的问题)

你可以

template <OpType T>
class Base{
 public:
  template <typename U, OpType X = T>
  typename std::enable_if<
             (X == OpType::INT && !std::is_same<U, bool>::value) 
             || 
             (X == OpType::BITWISE && std::is_same<U, bool>::value), U
           >::type
  Compute(U a, U b); 
};

using AddOperation = Base<OpType::INT>;

template<>
template<typename U, OpType X>
typename std::enable_if<
           (X == OpType::INT && !std::is_same<U, bool>::value) 
           || 
           (X == OpType::BITWISE && std::is_same<U, bool>::value), U
         >::type
AddOperation::Compute(U a, U b){ 
  std::cout << a << "," << b << std::endl;
  return a + b;
}

using AndOperation = Base<OpType::BITWISE>;

template<>
template<typename U, OpType X>
typename std::enable_if<
           (X == OpType::INT && !std::is_same<U, bool>::value) 
           || 
           (X == OpType::BITWISE && std::is_same<U, bool>::value), U
         >::type
AndOperation::Compute(U a, U b){
  return a & b;
}

然后

std::cout << b.Compute<int>(10, 2) << std::endl;       // fine
std::cout << a.Compute<bool>(true, true) << std::endl; // fine
std::cout << b.Compute<bool>(true, true) << std::endl; // error, no matching function
std::cout << a.Compute<int>(10, 2) << std::endl;       // error, no matching function

LIVE

另一种方法是class template specialization,将OpType::INTOpType::BITWISE的实现分开。

如果打算仅为 bool 类型禁用它,请为主要 class 模板 Base<T> 实施该方法。您也需要在 Compute() 的声明中使用 enable_if

#include <iostream>

template <typename T>
class Base{
 public:
  template <typename U>
  typename std::enable_if<!std::is_same<U, bool>::value, U>::type Compute(U a, U b);
};

template<typename T>
template<typename U>
typename std::enable_if<!std::is_same<U, bool>::value, U>::type
Base<T>::Compute(U a, U b){
  return a + b;
}

int main(){
  Base<int> b;
  std::cout << b.Compute<int>(10, 2) << std::endl;
  //std::cout << b.Compute<bool>(true, false) << std::endl; --> compile error
  return 0;
}

EDIT1

Base class 进行 class 模板专业化以获得结果。为 OpType::BITWISE 创建一个完整的专业化并实现你的 Compute 功能。

#include <iostream>

enum class OpType{
  INT,
  BITWISE,
};

template <OpType T>
class Base{
 public:
  template <typename U>
  typename std::enable_if<!std::is_same<U, bool>::value, U>::type Compute(U a, U b);
};

template<OpType T>
template<typename U>
typename std::enable_if<!std::is_same<U, bool>::value, U>::type
Base<T>::Compute(U a, U b){
  return a + b;
}

template <>
struct Base<OpType::BITWISE> {
  template <typename U>
  U Compute(U a, U b) { return a & b;}
};

using AddOperation = Base<OpType::INT>;
using AndOperation = Base<OpType::BITWISE>;

int main(){
  AddOperation b;
  AndOperation a;
  std::cout << b.Compute<int>(10, 2) << std::endl;
  std::cout << a.Compute<bool>(true, true) << std::endl;
  return 0;
}

这并不是你问题的真正答案,但为了便于阅读,有一种更好的方法可以禁止 bool 函数的 Compute( T , T ) 参数。

template <typename T>
class Base{
 public:
  template <typename U>
  U Compute(U a, U b);

  bool Compute(bool , bool) = delete;
};