Как преднамеренно вызвать ошибку во время компиляции при создании шаблона - PullRequest
25 голосов
/ 13 октября 2010

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

Пример:

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 (хотя я бы сказал, что тогда они заслуживают любых проблем, возникающих в результате).Но если вы используете слишком хитрый трюк, вы, скорее всего, получите сообщение об ошибке нерасшифруемого и / или вводящего в заблуждение компилятора, которое в основном не соответствует цели.

Существуют ли более простые и понятные способы запретить создание экземпляров шаблона?

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

Ответы [ 6 ]

27 голосов
/ 13 октября 2010

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

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

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

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

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

template<> struct MyClassTemplate<int*>;
14 голосов
/ 13 октября 2010

Для меня это звучит как типичный случай для static_assert из C ++ 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 };  
};

HTH

Редактировать: Исправлены примеры, чтобы они действительно работали ;-) Спасибо GMan!

2 голосов
/ 13 октября 2010

Если вы не хотите использовать библиотеку, эта конструкция довольно надежна (это примерно то, что 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 получить определение откуда-то, но вы могли бы использовать предварительное объявление, чтобы спрятать его в частном пространстве имен, если вы действительно этого хотите.

1 голос
/ 14 октября 2010

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

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

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
1 голос
/ 13 октября 2010

Понятия были удалены из '0x.Вы можете использовать библиотеку, например Boost Concept Check .

0 голосов
/ 13 октября 2010

boost::enable_if

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...