Если вы хотите сделать это самостоятельно (вместо использования чего-то вроде BOOST_STATIC _ASSERT), есть два или три основных трюка, которые обычно используются.
Первый (и, вероятно, самый важный, в вашемcase) должен использовать sizeof
(обычно с результатом void
), чтобы получить некоторый код, скомпилированный без , производящий что-либо, что будет выполняться во время компиляции.
Второйчтобы создать код, который является незаконным при правильных обстоятельствах.Одним из типичных методов является создание массива, размер которого будет равен значению некоторого выражения.Если выражение имеет значение 0, массив будет иметь размер 0, что недопустимо.В качестве альтернативы, если размер равен единице, это будет законно.Единственная проблема в том, что сообщение об ошибке, которое оно выдает, обычно довольно бессмысленно - трудно догадаться, как «ошибка: массив должен иметь положительный размер» (или что-то в этом роде) относится к «параметру шаблона не должен быть указатель».
Чтобы создать более значимое сообщение об ошибке, вы обычно используете немного другой прием.В этом случае преобразование из одного класса в другой завершится неудачей, если выражение ложно, но успешно, если оно истинно.Один из способов сделать это так:
template <bool>
struct check { check(...); };
template <>
class check<false> {};
check(...);
означает, что любой другой тип может (теоретически) быть преобразован в check<true>
(но обратите внимание, что мы объявляем толькофункция, никогда не определяйте ее, поэтому, если вы попытаетесь выполнить такой код, он не будет ссылаться).Отсутствие любого конструктора преобразования в check<false>
означает, что попытка преобразовать что-либо еще в check<false>
всегда будет неудачной.
Затем мы можем использовать это с макросом примерно так:
#define STATIC_ASSERT(expr, msg) { \
struct Error_##msg {}; \
(void)sizeof(check<(expr)!=0>((Error_##msg))); \
}
Вы бы использовали что-то вроде: STATIC_ASSERT(whatever, parameter_cannot_be_a_pointer);
.Это будет выглядеть примерно так:
struct Error_parameter_cannot_be_a_pointer {};
(void)sizeof(check<(expr)!=0>(Error_parameter_cannot_be_a_pointer);
Затем, если expr
! = 0, он попытается преобразовать Error_parameter_cannot_be_a_pointer в check<true>
, что будет успешно выполнено.
Вкл.с другой стороны, если expr
делает равным 0, он попытается преобразовать в check<false>
, что не удастся.Мы, по крайней мере, надеемся, что когда это произойдет, мы получим сообщение об ошибке, похожее на это:
error cannot convert:
Error_parameter_cannot_be_a_pointer
to
check<false>
Очевидно, что мы хотели бы получить еще лучшее сообщение, чем это, если бы могло , но дажев нынешнем виде это не так уж страшно.Вам просто нужно проигнорировать «обтекание» и посмотреть на имя типа источника, чтобы получить довольно хорошее представление о проблеме.