Как я могу создать утверждение времени компиляции, что шаблон имеет определенные типы? - PullRequest
5 голосов
/ 15 декабря 2011

У меня есть шаблонная функция, и я хочу во время компиляции убедиться, что она не реализована для подтипа или супертипа определенного класса.

Как я могу вызвать ошибку компилятора C ++, если она нарушена?

class base {
};
class derived : public base {
};
class lowest : public derived {
};

template <typename T>
bool isCorrect(const T& obj) {
  typedef foo<T> D;
  foo<T> *def = foo<T>::find();
  return (def && def->getAnswer(object));
}

Я хочу, чтобы isCorrect был доступен только для класса derived, но не base или lowest. Обратите внимание, что может быть много других низших классов и ряд базовых классов, которые будут исключены, а также альтернативные производные классы, которые являются приемлемыми.

Есть ли в C ++ способ ограничить использование шаблона только теми производными классами, которые я явно указал?

Ответы [ 3 ]

9 голосов
/ 15 декабря 2011

Тип черт, в частности is_base_of.

#include <type_traits>

template <typename T>
bool isCorrect(const T& obj) {
  static bool const is_base = std::is_base_of<base, T>::value;
  static bool const derives = std::is_base_of<derived, T>::value;
  // specify allowed types here
  static bool const is_derived = std::is_same<T, derived>::value;
  // ---
  static_assert((!is_base && !derives) || is_derived, "wrong argument type");

  typedef foo<T> D;
  foo<T> *def = foo<T>::find();
  return (def && def->getAnswer(object));
}

Обратите внимание, что это специфично для C ++ 11, но вы можете получить то же поведение с Boost.TypeTraits.

4 голосов
/ 15 декабря 2011

Вот одна из техник, о которых я знаю.

Сначала создайте еще один шаблонный класс policy_enforcer.Объявите этот класс , не определяя его , а также предоставьте его специализацию для derived , который также определен :

template<typename T> struct policy_enforcer;
template<> struct policy_enforcer<derived> { };

Затем в функции, которую выхотите заблокировать, включите выражение sizeof(policy_enforcer<T>).Поскольку sizeof для неполных типов является ошибкой компиляции, это предотвратит компиляцию кода.

Обновлен с действующим кодом: с использованием base, используя derived, используя lowest.

1 голос
/ 15 декабря 2011

Вы можете использовать шаблон специализации.

Вы можете реализовать только isCorrect только для типов, с которыми вы хотите работать.

Для других типов вы можете реализовать фиктивный метод, возвращая, например, false, или не реализовывать isCorrect вообще, в этом случае он не будет компилироваться для других типов.

#include <iostream>

using namespace std;

class base {
};
class derived : public base {
};
class lowest : public derived {
};

// using this it will fail if you try to pass anything
// else than `derived`
//  template <typename T>
//     bool isCorrect(const T& obj);

template <typename T>
bool isCorrect(const T& obj) {
    cout << __PRETTY_FUNCTION__ << endl;
    return false;
}

template <>
bool isCorrect<derived>(const derived& obj) {
    cout << __PRETTY_FUNCTION__ << endl;
    return true;
//  typedef foo<derived> D;
//  foo<derived> *def = foo<derived>::find();
//  return (def && def->getAnswer(object));
}

Тест:

int main()
{
    base b;
    derived d;
    lowest l;

    cout << isCorrect(b) << endl;
    cout << isCorrect(d) << endl;
    cout << isCorrect(l) << endl;
}

Выход:

bool isCorrect(const T&) [with T = base]
0
bool isCorrect(const T&) [with T = derived]
1
bool isCorrect(const T&) [with T = lowest]
0
...