static assert for const variables?
Static asserts are very convenient for checking things in compile time. A simple static assert idiom looks like this:
template<bool> struct StaticAssert;
template<> struct StaticAssert<true> {};
#define STATIC_ASSERT(condition) do { StaticAssert<(condition)>(); } while(0)
This is good for stuff like
STATIC_ASSERT(sizeof(float) == 4)
and:
#define THIS_LIMIT (1000)
...
STATIC_ASSERT(THIS_LIMIT > OTHER_LIMIT);
But using #define
is not the "C++" way of defining constants. C++ would have you use an anonymous namespace:
namespace {
const int THIS_LIMIT = 1000;
}
or even:
static const int THIS_LIMIT = 1000;
The trouble with this is that with a const int
you can't use STATIC_ASSERT()
and you must resort to a run-time check which is silly.
Is there a way to properly solve this in current C++?
I think I've read C++0x has some facility to do this...EDIT
Ok so this
static const int THIS_LIMIT = 1000;
...
STATIC_ASSERT(THIS_LIMIT > 0);
compiles fine
But this:static con开发者_如何学JAVAst float THIS_LIMIT = 1000.0f;
...
STATIC_ASSERT(THIS_LIMIT > 0.0f);
does not.
(in Visual Studio 2008)How come?
Why, you can still static assert with const int:
#define static_assert(e) extern char (*ct_assert(void)) [sizeof(char[1 - 2*!(e)])]
static_assert( THIS_LIMIT > OTHER_LIMIT )
Also, use boost!
BOOST_STATIC_ASSERT( THIS_LIMIT > OTHER_LIMIT )
... you'll get a lot nicer error messages...
static_assert
is a compiler feature in C++0x so as long as you've got a relatively up-to-date compiler you can use that. Watch out for doing #define static_assert(x) ...
, because it's a real keyword in C++0x so you'd be permanently hiding the compiler feature. Also, C++0x static_assert
takes two parameters (eg. static_assert(sizeof(int) == 4, "Expecting int to be 4 bytes")
), so you could cause yourself problems trying to switch in future if you use that #define.
It seems that you're really asking why the following is the case (and I can confirm that in both GCC 4.3.4 and 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
}
There are number of restrictions on using floating-point values statically. Note, for example, that you cannot pass them as template arguments. That is because:
[C++11: 5.19/2]:
A conditional-expression is a core constant expression unless it involves one of the following as a potentially evaluated subexpression (3.2), but subexpressions of logical AND (5.14), logical OR (5.15), and conditional (5.16) operations that are not evaluated are not considered [ Note: An overloaded operator invokes a function. —end note ]:
- [..]
- an lvalue-to-rvalue conversion (4.1) unless it is applied to
- a glvalue of integral or enumeration type that refers to a non-volatile const object with a preceding initialization, initialized with a constant expression, or
- a glvalue of literal type that refers to a non-volatile object defined with constexpr, or that refers to a sub-object of such an object, or
- a glvalue of literal type that refers to a non-volatile temporary object whose lifetime has not ended, initialized with a constant expression;
- [..]
(i.e. only integral and enumeration types are allowed; no floating-point types.)
As for the reason for this rule, I'm not entirely sure, but the following sort of rationale may well have something to do with it:
[C++11: 5.19/4]:
[..] Since this International Standard imposes no restrictions on the accuracy of floating-point operations, it is unspecified whether the evaluation of a floating-point expression during translation yields the same result as the evaluation of the same expression (or the same operations on the same values) during program execution. [..]
Perhaps you're confusing C++'s behavior with C, where const int
does not represent a true compile-time constant. Or perhaps your C++ compiler is broken. If it's truly the latter, use enum
instead.
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);
}
compiles fine with VC and Comeau.
enum{THIS_LIMIT = 1000};
精彩评论