статическое утверждение для константных переменных? - PullRequest
6 голосов
/ 25 мая 2010

Статические утверждения очень удобны для проверки вещей во время компиляции. Простая идиома статического утверждения выглядит следующим образом:

template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};

#define STATIC_ASSERT(condition) do { StaticAssert<(condition)>(); } while(0)

Это хорошо для таких вещей, как

STATIC_ASSERT(sizeof(float) == 4)

и

#define THIS_LIMIT (1000)
...
STATIC_ASSERT(THIS_LIMIT > OTHER_LIMIT);

Но использование #define не является способом определения констант "C ++". C ++ попросил бы вас использовать анонимное пространство имен:

namespace {
    const int THIS_LIMIT = 1000;
}

или даже:

static const int THIS_LIMIT = 1000;

Проблема в том, что с const int вы не можете использовать STATIC_ASSERT(), и вы должны прибегнуть к проверке во время выполнения, которая глупа.

Есть ли способ правильно решить эту проблему в текущем C ++?
Я думаю, что я прочитал C ++ 0x есть возможность сделать это ...


EDIT

Хорошо, так что это

static const int THIS_LIMIT = 1000;
...
STATIC_ASSERT(THIS_LIMIT > 0);

компилирует нормально
Но это:

static const float THIS_LIMIT = 1000.0f;
...
STATIC_ASSERT(THIS_LIMIT > 0.0f);

нет.
(в Visual Studio 2008)

Как получилось?

Ответы [ 6 ]

11 голосов
/ 25 мая 2010

Почему вы все еще можете статически утверждать с помощью const int:

#define static_assert(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])]
static_assert( THIS_LIMIT > OTHER_LIMIT )

Также используйте повышение !

BOOST_STATIC_ASSERT( THIS_LIMIT > OTHER_LIMIT )

... вы получите лот более приятные сообщения об ошибках ...

5 голосов
/ 25 мая 2010

static_assert - это функция компилятора в C ++ 0x, поэтому, если у вас есть относительно современный компилятор, вы можете использовать его. Не упустите #define static_assert(x) ..., потому что это реальное ключевое слово в C ++ 0x, так что вы бы навсегда скрыли функцию компилятора. Кроме того, C ++ 0x static_assert принимает два параметра (например, static_assert(sizeof(int) == 4, "Expecting int to be 4 bytes")), поэтому вы можете столкнуться с проблемами при попытке переключения в будущем, если будете использовать этот # define.

2 голосов
/ 22 января 2012

Похоже, что вы действительно спрашиваете, почему следующее (и я могу подтвердить это как в GCC 4.3.4, так и в Visual C ++ 2008 Express):

template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};

#define STATIC_ASSERT(condition) do { StaticAssert<(condition)>(); } while(0)


static const int   AN_INT  = 1000;
static const float A_FLOAT = 1000.0f;

int main()
{
   STATIC_ASSERT(AN_INT > 0);     // OK
   STATIC_ASSERT(A_FLOAT > 0.0f); // Error: A_FLOAT may not appear in a constant expression
}

Существует ряд ограничений на статическое использование значений с плавающей точкой. Обратите внимание, например, что вы не можете передать их в качестве аргументов шаблона. Это потому что:

[C++11: 5.19/2]: A условное выражение является выражением основной константы, если оно не включает одно из следующих значений в качестве потенциально вычисляемого подвыражения (3.2), но подвыражения логического И (5.14) ), логические операции ИЛИ (5.15) и условные операции (5.16), которые не оцениваются, не учитываются [ Примечание: Перегруженный оператор вызывает функция. - Конечная заметка ] :

  • [..]
  • преобразование lvalue в rvalue (4.1), если оно не применяется к
    • glvalue целочисленного или перечислимого типа , который относится к энергонезависимому константному объекту с предшествующей инициализацией, инициализированным с помощью константного выражения, или
    • glvalue литерального типа, который относится к энергонезависимому объекту, определенному с помощью constexpr, или который относится к подобъекту такого объекта, или
    • glvalue литерального типа, который относится к энергонезависимому временному объекту, время жизни которого не закончилось, инициализируется постоянным выражением;
  • [..]

(т. Е. Допускаются только целочисленные и перечислимые типы; типы с плавающей точкой не допускаются.)

Что касается причины для этого правила, я не совсем уверен, но следующий вид обоснования вполне может иметь к этому отношение:

[C++11: 5.19/4]: [..] Поскольку настоящий международный стандарт не устанавливает ограничений на точность операций с плавающей запятой, не определено, дает ли оценка выражения с плавающей запятой во время перевода тот же результат, что и оценка того же выражения (или тех же операций с теми же значениями) во время выполнения программы . [..]

1 голос
/ 25 мая 2010

This:

namespace {
    const int THIS_LIMIT = 1000;
}

template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};

#define STATIC_ASSERT(condition) do { StaticAssert<(condition)>(); } while(0)

int main()
{
    STATIC_ASSERT(THIS_LIMIT > 5);

    return (0);
}

прекрасно компилируется с VC и Comeau.

1 голос
/ 25 мая 2010

Возможно, вы путаете поведение C ++ с C, где const int не представляет истинную константу времени компиляции. Или, возможно, ваш компилятор C ++ не работает. Если это действительно последнее, используйте enum вместо.

0 голосов
/ 25 мая 2010

enum{THIS_LIMIT = 1000};

...