Есть ли способ создать функцию, которая принимает параметр шаблона int, и чтобы эта функция выдавала ошибку времени компиляции, если значение, переданное в функцию, меньше 10?
Следующий код не работает, но показывает, чего я хочу добиться:
template <int number1>
void reportErrorIfLessThan10()
{
#if (number1 < 10)
#error the number is less than 10
#endif
}
int maint(int argc, char**argv)
{
reportErrorIfLessThan10<5>();//report an error!
reportErrorIfLessThan10<12>();//ok
return 0;
}





template <int number1>
typename boost::enable_if_c< (number1 >= 10) >::type
reportErrorIfLessThan10() {
// ...
}
Вышеупомянутый enable_if без _c, потому что у нас простой bool, выглядит так:
template<bool C, typename T = void>
struct enable_if {
typedef T type;
};
template<typename T>
struct enable_if<false, T> { };
enable_ifУвеличение принимает не простой логический тип, поэтому у них есть другая версия с добавленным _c, которая принимает простые логические значения. Вы не сможете вызвать его для number1 <10. СФИНАЕ исключит этот шаблон как возможных кандидатов, потому что enable_if не будет предоставлять тип ::type, если условие оценивается как false. Если вы по какой-то причине хотите протестировать его в функции, то, если у вас есть возможность C++ 1x, вы можете использовать static_assert:
template <int number1>
void reportErrorIfLessThan10() {
static_assert(number >= 10, "number must be >= 10");
}
Если нет, вы можете использовать BOOST_STATIC_ASSERT:
template <int number1>
void reportErrorIfLessThan10() {
BOOST_STATIC_ASSERT(number >= 10);
}
Однако единственный способ отобразить описательное сообщение - использовать static_assert. Вы можете более или менее смоделировать это, используя типы, имена которых описывают состояние ошибки:
namespace detail {
/* chooses type A if cond == true, chooses type B if cond == false */
template <bool cond, typename A, typename B>
struct Condition {
typedef A type;
};
template <typename A, typename B>
struct Condition<false, A, B> {
typedef B type;
};
struct number1_greater_than_10;
}
template <int number1>
void reportErrorIfLessThan10() {
// number1 must be greater than 10
sizeof( typename detail::Condition< (number1 >= 10),
char,
detail::number1_greater_than_10
>::type );
}
Он печатает это здесь:
error: invalid application of 'sizeof' to incomplete type 'detail::number1_greater_than_10'
Но я думаю, что самый первый подход с использованием enable_if сделает это. Вы получите сообщение об ошибке необъявленного reportErrorIfLessThan10.
Если вам не нужна магия Библиотеки Boost C++ и вам нужны голые кости ...
template<bool> class static_check
{
};
template<> class static_check<false>
{
private: static_check();
};
#define StaticAssert(test) static_check<(test) != 0>()
Затем используйте StaticAssert. Для меня это #define, потому что у меня есть код, который нужно запускать во многих средах, где C++ не подходит для шаблонов, и мне нужно просто вернуть его в утверждение во время выполнения. :(
Также не самые лучшие сообщения об ошибках.
litb и Джо уже дали ответы, используемые на практике. Просто чтобы проиллюстрировать, как это можно сделать вручную, специализируясь на рассматриваемом числе (а не на общем логическом условии):
template <int N>
struct helper : helper<N - 1> { };
template <>
struct helper<10> { typedef void type; };
template <>
struct helper<0> { }; // Notice: missing typedef.
template <int N>
typename helper<N>::type error_if_less_than_10() {
}
int main() {
error_if_less_than_10<10>();
error_if_less_than_10<9>();
}
Функции не могут быть унаследованы, но классы (и структуры) могут. Следовательно, этот код также использует структуру, которая автоматически и динамически генерирует регистры для всех N, кроме 10 и 0, которые являются началом жестко запрограммированной рекурсии.
Кстати, приведенный выше код действительно дает довольно приятные сообщения об ошибках:
x.cpp:16: error: no matching function for call to 'error_if_less_than_10()'
Просто помните, что у большинства компиляторов есть ограничение на глубину рекурсии, которую вы делаете. В прошлый раз, когда я проверил, GCC по умолчанию установил 50 или около того. Однако я не знаю, верно ли то же самое для этого типа наследования.
Что ж, этот тест линейный. То есть вы получите экземпляры для каждого N от вашего ввода до 10 или 0. Поэтому, если вы установите N = 20000, вы получите 19990 базовых классов. Однажды я разбил свою операционную систему, пытаясь создать слишком много экземпляров шаблонов. как минимум требуется -ftemplate-depth-XXX :)
Да, как я уже сказал, этот код больше для иллюстративных целей, чем для чего-либо еще. Нет никаких причин (технических или иных) предпочесть это решение другим.
Если по какой-то причине вы не можете использовать Boost, этот пример тривиально записывается так:
template <int number1>
void reportErrorIfLessThan10()
{
typedef char number1_gt_10[number1 > 10 ? 1 : -1];
}
int maint(int argc, char**argv)
{
reportErrorIfLessThan10<5>();//report an error!
reportErrorIfLessThan10<12>();//ok
return 0;
}
Или более общий
#define static_assert(test, message) typedef char static_assert_at_ ## __LINE__[(test) ? 1 : -1];
Я не объединяю само сообщение об ошибке, потому что считаю, что static_assert(true, "some message"); более читабелен, чем, скажем, static_assert(true, some_message);. Однако это ограничивает вариант использования только одним утверждением на строку.
Границы массива необходимо переместить в конец строки. Также точка с запятой.
После всех этих лет C++ у меня все еще возникают проблемы с обычным typedef. Фу.
Этот код #define мне не подходит: компилятор проверяет синтаксис на ложные ветки. Метод специализации шаблона работает лучше.
Вы всегда можете использовать typedef с отрицательным суб-скриптом, как я опубликовал, если вы не можете использовать шаблоны. Что-то вроде #define StaticAssert (test) typedef char static_assert ## __LINE __ [(test)? -1: 1];