7

静态断言对于在编译时进行检查非常方便。一个简单的静态断言习惯用法如下所示:

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

但是 using#define不是定义常量的“C++”方式。C++ 会让你使用匿名命名空间:

namespace {
    const int THIS_LIMIT = 1000;
}

甚至:

static const int THIS_LIMIT = 1000;

这样做的问题是,const int你不能使用a STATIC_ASSERT(),你必须求助于运行时检查,这很愚蠢。

有没有办法在当前的 C++ 中正确解决这个问题?
我想我读过 C++0x 有一些工具可以做到这一点......


编辑

好的,所以这个

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 中)

怎么会?

4

6 回答 6

11

为什么,您仍然可以使用 const int 进行静态断言:

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

另外,使用 boost

BOOST_STATIC_ASSERT( THIS_LIMIT > OTHER_LIMIT )

...您会收到很多更好的错误消息...

于 2010-05-25T08:00:46.877 回答
5

static_assert是 C++0x 中的一个编译器特性,所以只要你有一个相对最新的编译器,你就可以使用它。注意 doing #define static_assert(x) ...,因为它是 C++0x 中的真正关键字,因此您将永久隐藏编译器功能。此外,C++0xstatic_assert采用两个参数(例如static_assert(sizeof(int) == 4, "Expecting int to be 4 bytes")),因此如果您使用该#define,将来尝试切换可能会导致问题。

于 2010-05-25T08:35:49.790 回答
2

看来您真的在问为什么会出现以下情况(我可以在 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]: 条件表达式是核心常量表达式,除非它涉及以下之一作为可能计算的子表达式(3.2),但逻辑与 (5.14)、逻辑或 (5.15) 和条件 (5.16) 操作的子表达式未计算不考虑 [注意:重载的运算符调用函数。——尾注]

  • [..]
  • 左值到右值的转换(4.1),除非它应用于
    • 一个整数或枚举类型的左值,它引用一个非易失性 const 对象,该对象具有前面的初始化,用常量表达式初始化,或
    • 一个字面量类型的左值,它引用一个用 constexpr 定义的非易失性对象,或者引用这样一个对象的子对象,或者
    • 一个字面量类型的左值,它引用一个生命周期尚未结束的非易失性临时对象,用一个常量表达式初始化;
  • [..]

(即只允许整数和枚举类型;不允许浮点类型。)

至于这条规则的原因,我并不完全确定,但下面的理由很可能与它有关:

[C++11: 5.19/4]:[..] 由于本国际标准对浮点运算的准确性没有任何限制,因此未指定在翻译期间对浮点表达式的求值是否与对相同表达式(或相同操作)的求值产生相同的结果在相同的值上)在程序执行期间。[..]

于 2012-01-21T22:10:56.367 回答
1

也许您将 C++ 的行为与 C 混淆了,Cconst int并不代表真正的编译时常量。或者您的 C++ 编译器可能已损坏。如果确实是后者,请enum改用。

于 2010-05-25T08:12:32.473 回答
1

这:

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 一起编译得很好。

于 2010-05-25T08:15:48.397 回答
0

enum{THIS_LIMIT = 1000};

于 2010-05-25T08:19:27.143 回答