недопустимое статическое поведение утверждения - PullRequest
2 голосов
/ 23 декабря 2009

Я пытаюсь настроить статическое утверждение (вне основной функции) с помощью GCC v4.3.x:

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])
STATIC_ASSERT( (double)1 == (double)1 ); // failed

но когда я использую числа с плавающей запятой, утверждение всегда завершается неудачей.

Можно ли правильно запустить это статическое утверждение?

Ответы [ 3 ]

9 голосов
/ 23 декабря 2009

C ++ Standard 2003, 5.19 "Постоянные выражения" , пункт 1.

В некоторых местах C ++ требует выражения, которые оценивают интеграл или константа перечисления: как границы массива (8.3.4, 5.3.4) , в зависимости от случая выражения (6.4.2) в виде битового поля длины (9.6), как перечислитель инициализаторы (7.2), как статический член инициализаторы (9.4.2), а так же интегральные или перечисление не тип шаблона аргументы (14.3).

постоянное выражение: условное выражение

интегральная константа-выражение банка задействовать только литералы (2.13), перечислители, константные переменные или статические члены данных интегрального или типы перечисления, инициализированные константные выражения (8.5), нетипичные параметры шаблона интегрального или типы перечисления и sizeof выражения. Плавающие литералы (2.13.3) могут появляться только если они приведение к целочисленному или перечислительному типу. Только преобразования типа в целое или типы перечисления могут быть использованы. В в частности, кроме размера выражения, функции, объекты класса, указатели или ссылки не должны быть используется и присваивание, приращение, декремент, вызов функции или запятая операторы не должны использоваться.

4 голосов
/ 23 декабря 2009

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

// would all work for example
STATIC_ASSERT( 1.0 == 1.0 );
STATIC_ASSERT( (int)1.0 == (int)1.0 );

Так что не само утверждение является недействительным и вызывает ошибку компилятора, это ваше приведение ...

Только для записи, Boost, конечно, имеет статическое утверждение тоже.

2 голосов
/ 23 декабря 2009

EDIT:

Действительно, перемещение STATIC_ASSERT из main() дает ошибку компилятора, поскольку приведение к типу, отличному от целочисленного или перечислимого, не может появиться в выражении-константе. Удаление приведений работает с GCC, но это не действительный ICE (как указано @AndreyT).

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])

STATIC_ASSERT( 1.0 == 1.0 );
STATIC_ASSERT( 1.0 != 1.0 ); // this is line 4

int main()
{
  return 0;
}

дает:

main.cpp: 4: ошибка: размер массива 'arg' отрицателен

Ссылка: ISO / IEC 14882 - 5.19 Выражения констант

целочисленное константное выражение может включать в себя только литералы (2.13), перечислители, const переменные или static члены-данные целочисленных типов или типов перечисления, инициализированные константными выражениями (8.5), не тип параметры шаблона целочисленного или перечислимого типов, а также размер выражений. Плавающие литералы (2.13.3) могут появляться, только если они приводятся к целочисленным или перечислимым типам. Можно использовать только преобразования типов в целочисленные или перечислимые типы. В частности, за исключением sizeof выражения, функции, объекты класса, указатели или ссылки не должны использоваться, а операторы присваивания, приращения, уменьшения, вызова функции или запятой не должны использоваться.


EDIT2: для записи, вот моя собственная реализация статических утверждений, извлеченных из моей базы кода: 1951741.cpp

#define CONCATENATE(arg1, arg2)   CONCATENATE1(arg1, arg2)
#define CONCATENATE1(arg1, arg2)  CONCATENATE2(arg1, arg2)
#define CONCATENATE2(arg1, arg2)  arg1##arg2

/**
 * Usage:
 *
 * <code>STATIC_ASSERT(expression, message)</code>
 *
 * When the static assertion test fails, a compiler error message that somehow
 * contains the "STATIC_ASSERTION_FAILED_AT_LINE_xxx_message" is generated.
 *
 * /!\ message has to be a valid C++ identifier, that is to say it must not
 * contain space characters, cannot start with a digit, etc.
 *
 * STATIC_ASSERT(true, this_message_will_never_be_displayed);
 */

#define STATIC_ASSERT(expression, message)\
  struct CONCATENATE(__static_assertion_at_line_, __LINE__)\
  {\
    implementation::StaticAssertion<static_cast<bool>((expression))> CONCATENATE(CONCATENATE(CONCATENATE(STATIC_ASSERTION_FAILED_AT_LINE_, __LINE__), _), message);\
  };\
  typedef implementation::StaticAssertionTest<sizeof(CONCATENATE(__static_assertion_at_line_, __LINE__))> CONCATENATE(__static_assertion_test_at_line_, __LINE__)

  // note that we wrap the non existing type inside a struct to avoid warning
  // messages about unused variables when static assertions are used at function
  // scope
  // the use of sizeof makes sure the assertion error is not ignored by SFINAE

namespace implementation {

  template <bool>
  struct StaticAssertion;

  template <>
  struct StaticAssertion<true>
  {
  }; // StaticAssertion<true>

  template<int i>
  struct StaticAssertionTest
  {
  }; // StaticAssertionTest<int>

} // namespace implementation


STATIC_ASSERT(1.0f == 1.0 , ok);
STATIC_ASSERT(1.0f != 1.0 , ko);

int main()
{
  return 0;
}

При использовании STATIC_ASSERT((float) 1 == (float) 1, must_be_true); выдает правильную ошибку:

main.cpp: 49: ошибка: приведение к типу, отличному от целочисленного или перечислимого, не может присутствовать в выражении-константе


Какой у вас вопрос?

#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])

int main()
{
  STATIC_ASSERT( (float)1 == (float)1 );
  STATIC_ASSERT( (float)1 != (float)1 ); // this is line 6
  return 0;
}

Компиляция с помощью gcc 4.4.2 дает мне:

main.cpp: в функции int main ():

main.cpp: 6: ошибка: размер массива 'arg' отрицателен

Так что да, (float)1 != (float)1 оценивается как false и заставляет ваш макрос STATIC_ASSERT использовать массив размером -1, который останавливает компиляцию.

...