Принудительная ошибка времени компиляции при вызове специальной функции шаблона - PullRequest
2 голосов
/ 13 ноября 2011

У меня есть функция шаблона.Он имеет четко определенную семантику, если аргумент не является типом указателя.Если кто-то вызывает эту функцию, передавая аргумент типа указатель, я хочу вызвать ошибку времени компиляции.У меня нет проблем с написанием общего (легального) шаблона и соответствующей частично специализированной (нелегальной) версии.Я просто не могу понять, как отложить ошибку от определения функции до вызова функции.

Ответы [ 4 ]

4 голосов
/ 13 ноября 2011

Использование C ++ 0x: (посмотреть вживую на http://ideone.com/ZMNb1)

#include <type_traits>
#include <iostream>

template <typename T>
    void cannot_take_pointer(T ptr)
{
    static_assert(!std::is_pointer<T>::value, 
        "cannot_take_pointer requires non-pointer argument");
    std::cout << "ok\n";
}

int main()
{
    int x;
    cannot_take_pointer(x);
    cannot_take_pointer(&x);  // fails to compile
}
2 голосов
/ 13 ноября 2011

Вам не нужно специализировать это, на самом деле.Просто добавьте это в ваше тело функции:

BOOST_STATIC_ASSERT(!boost::is_pointer<T>()::value);

Это вызовет сбой, который должен быть довольно легко понять.

1 голос
/ 13 ноября 2011

звучит как идеальный случай для boost::disable_if. Нечто подобное должно работать.

template <class T>
void func(T x, typename boost::disable_if<boost::is_pointer<T> >::type* dummy = 0) {
    std::cout << x << std::endl;
}


func(10); // works
func(std::string("hello")); // works
func("hello world"); // error: no matching function for call to 'func(const char [6])'
func(new int(10)); // error: no matching function for call to 'func(int*&)'
1 голос
/ 13 ноября 2011

Если вы хотите сделать это самостоятельно (вместо использования чего-то вроде 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>

Очевидно, что мы хотели бы получить еще лучшее сообщение, чем это, если бы могло , но дажев нынешнем виде это не так уж страшно.Вам просто нужно проигнорировать «обтекание» и посмотреть на имя типа источника, чтобы получить довольно хорошее представление о проблеме.

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