Что делает static_assert и для чего вы его используете? - PullRequest
104 голосов
/ 30 октября 2009

Не могли бы вы привести пример, где static_assert(...) 'C++0x' изящно решит проблему?

Я знаком со временем выполнения assert(...). Когда я должен предпочесть static_assert(...) обычному assert(...)?

Кроме того, в boost есть нечто, называемое BOOST_STATIC_ASSERT, это то же самое, что static_assert(...)?

Ответы [ 7 ]

119 голосов
/ 30 октября 2009

Статическое утверждение используется для создания утверждений во время компиляции. Когда статическое утверждение не выполняется, программа просто не компилируется. Это полезно в различных ситуациях, например, если вы реализуете некоторые функции с помощью кода, который критически зависит от объекта 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.

67 голосов
/ 30 октября 2009

С макушки моей головы ...

#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.

@ Арак, в ответ на ваш комментарий: да, вы можете 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 :("
12 голосов
/ 30 октября 2009

Я использую его, чтобы убедиться, что мои предположения о поведении компилятора, заголовках, библиотеках и даже моем собственном коде верны. Например, здесь я проверяю, что структура была правильно упакована до ожидаемого размера.

struct LogicalBlockAddress
{
#pragma pack(push, 1)
    Uint32 logicalBlockNumber;
    Uint16 partitionReferenceNumber;
#pragma pack(pop)
};
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6);

В классовой упаковке stdio.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 стандартизирован и может обеспечить лучшую диагностику компилятора.

9 голосов
/ 12 января 2010

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 ... */
};

Это приведет к ошибке времени компиляции, если какое-либо из указанных выше условий не будет выполнено.

5 голосов
/ 30 октября 2009

Одно из применений static_assert может заключаться в том, чтобы гарантировать, что структура (то есть интерфейс с внешним миром, таким как сеть или файл) точно соответствует ожидаемому размеру. Это будет отлавливать случаи, когда кто-то добавляет или изменяет член из структуры, не осознавая последствий. static_assert поднимет его и предупредит пользователя.

3 голосов
/ 14 сентября 2015

В отсутствие концепций можно использовать static_assert для простой и удобочитаемой проверки типов во время компиляции, например, в шаблонах:

template <class T>
void MyFunc(T value)
{
static_assert(std::is_base_of<MyBase, T>::value, 
              "T must be derived from MyBase");

// ...
}
2 голосов
/ 02 июня 2014

Это не дает прямого ответа на исходный вопрос, но делает интересное исследование о том, как применять эти проверки времени компиляции до C ++ 11.

Глава 2 (Раздел 2.1) из Современный дизайн C ++ от Andrei Alexanderscu реализует эту идею утверждений времени компиляции следующим образом

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");
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...