Заголовок<<boost/static_assert.hpp>
>содержит два макроса:
BOOST_STATIC_ASSERT(x)
BOOST_STATIC_ASSERT_MSG(x, msg)
Оба создают сообщение об ошибке компиляции времени, если интегрально-постоянное выражение<x
>не соответствует действительности. Другими словами, они являются компиляционным временным эквивалентом макроса утверждения; это иногда известно как «утверждение о времени компиля», но будет называться «статическим утверждением» во всех этих документах. Обратите внимание, что если условие<true
>, то макросы не будут генерировать ни код, ни данные - и макросы также могут использоваться в любом пространстве имен, классе или области функций. При использовании в шаблоне статическое утверждение будет оцениваться в момент создания шаблона; это особенно полезно для проверки параметров шаблона.
Если C++0x<static_assert
>функция доступна, оба макроса будут использовать ее. Для<BOOST_STATIC_ASSERT(x)
>сообщение об ошибке будет строгой версией<x
>. Для<BOOST_STATIC_ASSERT_MSG(x,
msg)
>сообщение об ошибке будет строкой<msg
>.
Если функция C++0x<static_assert
>недоступна,<BOOST_STATIC_ASSERT_MSG(x,
msg)
>будет рассматриваться как<BOOST_STATIC_ASSERT(x)
>.
Следующий материал предполагает, что функция C++0x<static_assert
>недоступна.
Одна из целей<BOOST_STATIC_ASSERT
>- генерировать читаемые сообщения об ошибках. Они сразу же сообщают пользователю, что библиотека используется таким образом, который не поддерживается. Хотя сообщения об ошибках, очевидно, отличаются от компилятора к компилятору, вы должны увидеть что-то вроде:
Illegal use of STATIC_ASSERTION_FAILURE<false>
Который должен хотя бы привлечь внимание!
Вы можете использовать<BOOST_STATIC_ASSERT
>в любом месте, где вы можете разместить декларацию, то есть в области класса, функции или пространства имен, это иллюстрируется следующими примерами:
Макрос может использоваться в области пространства имен, если есть какое-то требование, которое всегда должно быть верным; как правило, это означает определенное требование платформы. Предположим, что<int
>должно быть по меньшей мере 32-битным интегральным типом, а<wchar_t
>— неподписанным типом. Мы можем проверить это во время составления следующим образом:
#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>
namespace my_conditions {
BOOST_STATIC_ASSERT(std::numeric_limits<int>::digits >= 32);
BOOST_STATIC_ASSERT(WCHAR_MIN >= 0);
}
Использование пространства имен my_conditions здесь требует некоторых комментариев. Макро<BOOST_STATIC_ASSERT
>работает, генерируя объявление типаdef, и поскольку типdef должен иметь имя, макрос автоматически генерирует его, искажая имя заглушки со значением<__LINE__
>. Когда<BOOST_STATIC_ASSERT
>используется в любом классе или функциональной области, то каждое использование<BOOST_STATIC_ASSERT
>гарантированно создает имя, уникальное для этой области (при условии, что вы используете макрос только один раз на каждой линии). Однако при использовании в заголовке в области пространства имен это пространство имен может быть продолжено над несколькими заголовками, каждый из которых может иметь свои собственные статические утверждения, и на «одинаковых» линиях, тем самым генерируя дублирующие декларации. Теоретически компилятор должен молча игнорировать повторяющиеся объявления типа DEF, однако многие не делают этого (и даже если они это делают, они имеют право выдавать предупреждения в таких случаях). Чтобы избежать потенциальных проблем, если вы используете<BOOST_STATIC_ASSERT
>в заголовке и в области пространства имен, то закройте их в пространстве имен, уникальном для этого заголовка.
Макрос обычно используется в области функций внутри функций шаблона, когда аргументы шаблона нуждаются в проверке. Представьте, что у нас есть алгоритм на основе итератора, который требует итераторов случайного доступа. Если алгоритм инстанцирован с итераторами, которые не соответствуют нашим требованиям, то в конечном итоге будет сгенерирована ошибка, но она может быть вложена глубоко внутри нескольких шаблонов, что затрудняет пользователю определение того, что пошло не так. Один из вариантов заключается в добавлении статического утверждения на верхнем уровне шаблона, в том случае, если условие не выполнено, то ошибка будет генерироваться таким образом, чтобы пользователю было достаточно очевидно, что шаблон используется неправильно.
#include <iterator>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>
template <class RandomAccessIterator >
RandomAccessIterator foo(RandomAccessIterator from,
RandomAccessIterator to)
{
typedef typename std::iterator_traits<
RandomAccessIterator >::iterator_category cat;
BOOST_STATIC_ASSERT(
(boost::is_convertible<
cat,
const std::random_access_iterator_tag&>::value));
return from;
}
Несколько сносок приведены здесь в порядке: дополнительный набор скобок вокруг утверждения, чтобы предотвратить интерпретацию запятой внутри шаблона<is_convertible
>препроцессором в качестве сепаратора макроаргумента; целевой тип для<is_convertible
>является эталонным типом, поскольку некоторые компиляторы имеют проблемы с использованием<is_convertible
>, когда преобразование осуществляется через конструктор, определенный пользователем (в любом случае нет никакой гарантии, что классы тегов итератора являются копируемыми).
Макро обычно используется внутри классов, которые являются шаблонами. Предположим, что у нас есть класс шаблонов, который требует неподписанного интегрального типа с по меньшей мере 16 битами точности в качестве аргумента шаблона, мы можем достичь этого, используя что-то вроде этого:
#include <limits>
#include <boost/static_assert.hpp>
template <class UnsignedInt>
class myclass
{
private:
BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::is_specialized, "myclass can only be specialized for types with numeric_limits support.");
BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::digits >= 16, "Template argument UnsignedInt must have at least 16 bits precision.")
BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::is_integer, "Template argument UnsignedInt must be an integer.");
BOOST_STATIC_ASSERT_MSG(!std::numeric_limits<UnsignedInt>::is_signed, "Template argument UnsignedInt must not be signed.");
public:
};
Обычно статические утверждения при использовании внутри шаблона класса или функции не будут инстанцированы до тех пор, пока шаблон, в котором они используются, не будет инстанцирован. Однако есть одна потенциальная проблема, на которую следует обратить внимание: если статическое утверждение не зависит от одного или нескольких параметров шаблона, то компилятору разрешается оценивать статическое утверждение в точке, в которой оно впервые видно, независимо от того, когда шаблон когда-либо создан, например:
template <class T>
struct must_not_be_instantiated
{
BOOST_STATIC_ASSERT(false);
};
Будет производиться ошибка компилятора с некоторыми компиляторами (например, Intel 8.1 или gcc 3.4), независимо от того, был ли когда-либо создан шаблон. Обход в подобных случаях заключается в том, чтобы заставить утверждение зависеть от шаблонного параметра:
template <class T>
struct must_not_be_instantiated
{
BOOST_STATIC_ASSERT(sizeof(T) == 0);
};