Как использовать enable_if для внепланового определения члена класса шаблона - PullRequest
2 голосов
/ 12 февраля 2020

Я пытаюсь понять использование 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;
}

Намерение: не хотите включать Compute для bool типа

, но в приведенном выше коде это работает. Как мне убедиться, что функция Compute для bool не специализируется компилятором?

EDIT1

Конечная цель - включить Compute для U = bool для T = T1 и отключить Compute для U = bool для T = T2. Вот еще один пример кода, с помощью которого я пытаюсь добиться того же

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

Ответы [ 3 ]

2 голосов
/ 12 февраля 2020

Вы также должны использовать 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 отклоняет ваш текущий код, как я ожидал, потому что объявление и определение не совпадают.

ошибка: внешнее определение 'Compute' не соответствует ни одному объявлению в '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

Другой подход - специализация шаблона класса , для разделения реализации OpType::INT и OpType::BITWISE.

1 голос
/ 12 февраля 2020

Это не совсем ответ на ваш вопрос, но есть лучший способ запретить bool аргумент для Compute( T , T ) функции для удобства чтения.

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

  bool Compute(bool , bool) = delete;
};
0 голосов
/ 12 февраля 2020

Если вы хотите отключить его только для типов bool, используйте метод, реализованный для шаблона основного класса Base<T>. Вам также нужно enable_if в объявлении Compute ().

#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, чтобы добиться результатов. Создайте полную специализацию для 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;
}
...