开发者

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};

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜