简体   繁体   中英

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

I am trying to understand the usage of enable_if but I do have few difficulties in the same. Here I have written a test code that doesn't seem to work as intended.

#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;
}

Intention: Don't want to enable Compute for bool type

but in the code above, it is working. How do I make sure that the Compute function for bool is not specialized by compiler?

EDIT1

Eventual goal is to enable Compute for U=bool for T=T1 and disable Compute for U=bool for T=T2. Here is another example code by which I am trying to achieve the same

#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;
}

You should use enable_if in the declaration too, as the definition did.

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

In fact, clang rejects your current code as I expected, because the declaration and defintion don't match.

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


EDIT (for your added question)

You can

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;
}

then

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

Another approach is class template specialization , to seperate the implementation of OpType::INT and OpType::BITWISE .

It is not really answer of your question but there is a better way to forbid bool argument for Compute( T, T ) function for sake of readability.

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

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

If the intention is to disable it only for bool types, have the method implemented for the primary class template Base<T> . You need enable_if in the declaration of Compute() too.

#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

Do class template specialization for Base class to achieve the results. Create a full specialization for OpType::BITWISE and implement your Compute function.

#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;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM