invalid static assert behavior
I am trying to setup a static assert (o开发者_开发知识库utside the main function) with GCC v4.3.x:
#define STATIC_ASSERT(cond) extern void static_assert(int arg[(cond) ? 1 : -1])
STATIC_ASSERT( (double)1 == (double)1 ); // failed
but when I use float numbers, the assert always failed.
Is it possible to run this static assert properly ?
C++ Standard 2003, 5.19 "Constant expressions", paragraph 1.
In several places, C++ requires expressions that evaluate to an integral or enumeration constant: as array bounds (8.3.4, 5.3.4), as case expressions (6.4.2), as bit-field lengths (9.6), as enumerator initializers (7.2), as static member initializers (9.4.2), and as integral or enumeration non-type template arguments (14.3).
constant-expression: conditional-expression
An integral constant-expression can involve only literals (2.13), enumerators, const variables or static data members of integral or enumeration types initialized with constant expressions (8.5), non-type tem- plate parameters of integral or enumeration types, and sizeof expressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except in sizeof expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
I think this has to do with the rule that a cast to anything but an integer or enumeration type can not appear in a constant expression.
// would all work for example
STATIC_ASSERT( 1.0 == 1.0 );
STATIC_ASSERT( (int)1.0 == (int)1.0 );
So it's not the assert itself that's invalid, and causes a compiler error, it's your cast...
Just for the record, boost, of course, has a static assert too.
EDIT:
Indeed, moving STATIC_ASSERT
out of main()
gives a compiler error because a cast to a type other than an integral or enumeration type cannot appear in a constant-expression. Removing the casts works with GCC still it's not a valid ICE (as pointed by @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;
}
gives:
main.cpp:4: error: size of array ‘arg’ is negative
Reference: ISO/IEC 14882 - 5.19 Constant Expressions
An integral constant-expression can involve only literals (2.13), enumerators,
const
variables orstatic
data members of integral or enumeration types initialized with constant expressions (8.5), non-type template parameters of integral or enumeration types, andsizeofexpressions. Floating literals (2.13.3) can appear only if they are cast to integral or enumeration types. Only type conversions to integral or enumeration types can be used. In particular, except insizeof
expressions, functions, class objects, pointers, or references shall not be used, and assignment, increment, decrement, function-call, or comma operators shall not be used.
EDIT2: for the record, here is my own implementation of static assertions extracted from my code base: 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;
}
When using STATIC_ASSERT((float) 1 == (float) 1, must_be_true);
it gives a proper error:
main.cpp:49: error: a cast to a type other than an integral or enumeration type cannot appear in a constant-expression
What is your question exactly?
#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;
}
Compiling it with gcc 4.4.2 gives me:
main.cpp: In function ‘int main()’:
main.cpp:6: error: size of array ‘arg’ is negative
So yes, (float)1 != (float)1
evaluates to false
and makes your STATIC_ASSERT
macro use an array of size -1
which stops compilation.
精彩评论