Как преднамеренно вызывать ошибку времени компиляции при создании шаблона

Иногда при кодировании с шаблонами С++ вы хотите запретить пользователям создавать экземпляр определенной специализации или набора специализаций, потому что результат будет бессмысленным. Таким образом, вы можете определить (конкретную или частичную) специализацию, чье определение, если оно было создано, вызовет ошибку компилятора. Целью было бы, если пользователь "злоупотребляет" шаблоном, выдает ошибку компилятора рядом с комментарием в вашем файле заголовка, объясняя, что не делать, вместо того, чтобы оставлять компилятор придумать какое-то запутанное сообщение об ошибке своим собственным устройств или, возможно, позволяет компилировать сомнительный код.

Пример:

template <typename T> struct MyClassTemplate {
  // ...
};

template <typename T> struct MyClassTemplate<T*> {
  // Do not use MyClassTemplate with a pointer type!
  typedef typename T::intentional_error err;
};

Существует несколько способов сделать это (в зависимости от того, является ли ваша специализация полной или частичной специализацией класса или функции). Но используемый синтаксис должен (?) Зависеть от параметра шаблона, иначе компилятор будет жаловаться, когда он сначала разбирает определение преднамеренной ошибки. В приведенном выше примере есть отверстие, в котором кто-то может упрямо определить вложенный тип intentional_error или тип typedef (хотя я бы сказал, что тогда они заслуживают любых проблем, возникающих в результате). Но если вы используете слишком увлекательный трюк, вы, вероятно, получите сообщение об ошибке, которое нечеткое и/или вводящее в заблуждение сообщение компилятора, которое в основном побеждает цель.

Есть ли более простые способы запретить создание шаблонов?

Я знаю, что в С++ 0x объявления шаблонов Concepts и удаленных функций обеспечат намного лучший контроль над такими вещами, но я ищу ответы, которые являются допустимыми С++ 03.

Ответ 1

Вы можете просто опустить определение.

template <typename T> struct MyClassTemplate<T*>;

Вы также можете получить не определенную специализацию

template <typename T> struct invalid;
template <typename T> struct MyClassTemplate<T*> : invalid<T> { };

Обратите внимание, что явные специализации, объявляющие классы или функции, никогда не будут зависеть от параметров шаблона. Таким образом, такие вещи, которые зависят от параметров шаблона, не могут работать в любом случае. В этом случае объявление неопределенной явной специализации должно быть достаточным

template<> struct MyClassTemplate<int*>;

Ответ 2

Для меня это звучит как типичный случай для static_assert из С++ 0x или BOOST_STATIC_ASSERT. Функция static_assert имеет то преимущество, что вы можете передать собственное сообщение об ошибке, чтобы причина ошибки была более понятной.

Оба способа дают вам возможность преждевременно прекратить процесс компиляции в рамках определенного пользовательского условия времени компиляции.

с static_assert:

template <typename T> struct MyClassTemplate<T*> {
    static_assert(always_false<T>::value, "Do not use MyClassTemplate with a pointer type!");
};

с BOOST_STATIC_ASSERT

template <typename T> struct MyClassTemplate<T*> {
    // Do not use MyClassTemplate with a pointer type!
    BOOST_STATIC_ASSERT(always_false<T>::value);
};

Всегда false будет выглядеть примерно так:

template< typename T >
struct always_false { 
    enum { value = false };  
};

НТН

Изменить: Исправлены примеры, чтобы заставить их работать;-) Благодаря GMan!

Ответ 4

Если вы не хотите использовать библиотеку, эта конструкция довольно надежна (это примерно то, что делает Boost внутри):

template <typename T>
void must_be_specialized(T const&)
{
   enum dummy { d = (sizeof(struct must_be_specialized_for_this_type)
                     == sizeof(T)) };
}

Вы можете поместить что-то аналогичное по специализации, чтобы запретить создание шаблона с этим типом. Я бы не стал лично беспокоиться о том, чтобы must_be_specialized_for_this_type получить определение откуда-то, но вы могли бы использовать декларацию forward, чтобы ее белка в частном пространстве имен, если вы действительно этого хотели.

Ответ 5

"Есть ли более простые способы запретить создание шаблонов?" Ничего значительно лучше, чем то, что вы уже определили. Я уверен, что механизмы защиты С++ существуют, чтобы защитить вас от случайности не от злобы. И кто-то, определяющий специализацию или класс, чтобы разорвать ваше намеренное использование, я считаю вредоносным. Возможно, вы могли ударить человека в затылок каждый раз, когда они это делают.

Я лично предпочитаю помещать чеки в шаблоны, которые существуют только для описания проверок. Это позволяет использовать интересные комбинации наследования и шаблонов.

template <class T>
class not_with_pointer_t { };

template <class T>
class not_with_pointer_t<T*>;

template <class T>
class some_class_t : public not_with_pointer_t<T> { };

template <class T, template <class U> class base_t>
class another_class_t : public base_t<T> { };

typedef some_class_t<int> a_t; // ok
typedef some_class_t<void*> b_t; // error if instantiated
typedef another_class_t<void*, not_with_pointer_t> c_t; // error if instantiated

template <class T> class unrestricted_t { };
typedef another_class_t<void*, unrestricted_t> d_t; // ok

Ответ 6

boost::enable_if