Что делает static_assert и для чего бы вы его использовали?
-
22-07-2019 - |
Вопрос
Не могли бы вы привести пример, где static_assert(...) 'C++0x'
могли бы вы элегантно решить стоящую перед вами проблему?
Я знаком со временем выполнения assert(...)
.Когда я должен предпочесть static_assert(...)
слишком регулярно assert(...)
?
Кроме того, в boost
есть нечто, называемое BOOST_STATIC_ASSERT
, это то же самое , что static_assert(...)
?
Решение
С макушки головы ...
#include "SomeLibrary.h"
static_assert(SomeLibrary::Version > 2,
"Old versions of SomeLibrary are missing the foo functionality. Cannot proceed!");
class UsingSomeLibrary {
// ...
};
Предполагая, что SomeLibrary :: Version
объявлен как статическая константа, а не как #define
d (как и следовало ожидать в библиотеке C ++).
Сравните с необходимостью фактически скомпилировать SomeLibrary
и ваш код, связать все и запустить только затем исполняемый файл, чтобы выяснить, что вы потратили 30 минут на компиляцию несовместимой версии <код> SomeLibrary код>.
@Arak, в ответ на ваш комментарий: да, у вас может быть static_assert
, где бы вы ни находились, по его виду:
class Foo
{
public:
static const int bar = 3;
};
static_assert(Foo::bar > 4, "Foo::bar is too small :(");
int main()
{
return Foo::bar;
}
$ g++ --std=c++0x a.cpp a.cpp:7: error: static assertion failed: "Foo::bar is too small :("
Другие советы
Статическое утверждение используется для создания утверждений во время компиляции. Когда статическое утверждение не выполняется, программа просто не компилируется. Это полезно в различных ситуациях, например, если вы реализуете некоторые функции с помощью кода, который критически зависит от объекта unsigned int
, имеющего ровно 32 бита. Вы можете поместить статическое утверждение, как это
static_assert(sizeof(unsigned int) * CHAR_BIT == 32);
в вашем коде. На другой платформе с типом unsigned int
другого размера компиляция завершится неудачно, что привлечет внимание разработчика к проблемной части кода и посоветует им повторно внедрить или повторно проверить его. р>
В другом примере вам может потребоваться передать некое интегральное значение в виде указателя void *
на функцию (взломать, но иногда полезно), и вы хотите убедиться, что интегральное значение будет вписаться в указатель
int i;
static_assert(sizeof(void *) >= sizeof i);
foo((void *) i);
Возможно, вы захотите использовать этот тип char
со знаком
static_assert(CHAR_MIN < 0);
или это интегральное деление с отрицательными значениями округляет до нуля
static_assert(-5 / 2 == -2);
И так далее.
Утверждения времени выполнения во многих случаях могут использоваться вместо статических утверждений, но утверждения времени выполнения работают только во время выполнения и только тогда, когда управление передает утверждение. По этой причине ошибочное утверждение во время выполнения может оставаться неактивным, необнаруженным в течение продолжительных периодов времени.
Конечно, выражение в статическом утверждении должно быть константой времени компиляции. Это не может быть значением времени выполнения. Для значений времени выполнения у вас нет другого выбора, кроме как использовать обычный assert
.
Я использую его, чтобы убедиться, что мои предположения о поведении компилятора, заголовках, библиотеках и даже моем собственном коде верны. Например, здесь я проверяю, что структура была правильно упакована до ожидаемого размера.
struct LogicalBlockAddress
{
#pragma pack(push, 1)
Uint32 logicalBlockNumber;
Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);
В stse.h
класса, обертывающем fseek ()
, я взял несколько ярлыков с помощью enum Origin
и проверил, что эти ярлыки выровнять с константами, определенными stdio.h
uint64_t BasicFile::seek(int64_t offset, enum Origin origin)
{
BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET);
Вы должны предпочесть static_assert
, а не assert
, когда поведение определяется во время компиляции, а не во время выполнения, как в приведенных выше примерах. Пример, в котором это не , будет включать проверку параметров и кода возврата.
BOOST_STATIC_ASSERT
- это макрос до C ++ 0x, который генерирует недопустимый код, если условие не выполняется. Намерения те же, хотя static_assert
стандартизирован и может обеспечить лучшую диагностику компилятора.
BOOST_STATIC_ASSERT
является кроссплатформенной оболочкой для static_assert
функциональность.
В настоящее время я использую static_assert для того, чтобы применить "Концепции" к классу.
пример:
template <typename T, typename U>
struct Type
{
BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value);
BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer);
/* ... more code ... */
};
Это вызовет ошибку времени компиляции, если какое-либо из вышеперечисленных условий не будет выполнено.
Одно из применений static_assert
может заключаться в том, чтобы гарантировать, что структура (то есть интерфейс с внешним миром, таким как сеть или файл) - это именно тот размер, который вы ожидаете. Это будет отлавливать случаи, когда кто-то добавляет или изменяет член из структуры, не осознавая последствий. static_assert
поднимет его и предупредит пользователя.
В отсутствие концепций можно использовать static_assert
для простой и удобочитаемой проверки типов во время компиляции, например, в шаблонах:
template <class T>
void MyFunc(T value)
{
static_assert(std::is_base_of<MyBase, T>::value,
"T must be derived from MyBase");
// ...
}
Это не дает прямого ответа на исходный вопрос, но делает интересное исследование о том, как применять эти проверки времени компиляции до C ++ 11.
Глава 2 (раздел 2.1) из современного дизайна C ++ Андрея Александерску реализует это Идея утверждений времени компиляции, как это
template<int> struct CompileTimeError;
template<> struct CompileTimeError<true> {};
#define STATIC_CHECK(expr, msg) \
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; }
Сравните макросы STATIC_CHECK () и static_assert ()
STATIC_CHECK(0, COMPILATION_FAILED);
static_assert(0, "compilation failed");