编译时断言?

时间:2011-07-20 17:13:09

标签: c++ compiler-construction

有没有办法可以断言两个常量表达式在编译时是相等的?

e.g。我希望这会导致编译时错误

enum { foo=263, bar=264 };
SOME_EXPRESSION(foo,bar)

但我希望这不会导致错误

enum { foo=263, bar=263 };
SOME_EXPRESSION(foo,bar)

编辑:上面简化了。我的情况更像是

some_other_file_I_dont_control.h:

class X
{
public:
   enum { foo=263 };
}

my_file.h:

enum { bar=something+somethingelse }; // bar should equal X::foo
SOME_EXPRESSION(X::foo, bar)

12 个答案:

答案 0 :(得分:34)

是。您可以使用bool类型的模板特化来完成此操作,如下所示:

// empty default template
template <bool b>
struct StaticAssert {};

// template specialized on true
template <>
struct StaticAssert<true>
{
    static void assert() {}
};

int f()
{
    StaticAssert<1==1>::assert();   // compiles fine, assert() member found
    StaticAssert<1==2>::assert();   // compile failure, no assert() member for StaticAssert<false>
}

代码基本上来自内存,可能需要一些调整。

答案 1 :(得分:33)

参见static_assert(仅限C ++ 0x);如果是旧版本,请参阅Boost's StaticAssert

答案 2 :(得分:22)

对于静态断言的另一个版本,您可以通过添加更好的名称来赞美,您可以使用:

// name must be a valid identifier
#define STATIC_ASSERT( condition, name )\
    typedef char assert_failed_ ## name [ (condition) ? 1 : -1 ];

并用作:

STATIC_ASSERT( x == y, constants_must_be_same );

编译器将触发类似于:

的错误
size of array 'assert_failed_constants_must_be_same' is negative

这似乎没有用,但是它会指向断言的确切行,过了一段时间,你将开始处理该错误消息 static assert failed

答案 3 :(得分:6)

Windows的另一种可能性是C_ASSERT,如果包含Windows.h,则会定义它。

答案 4 :(得分:2)

还有使用switch (..)语句的技巧。虽然有点旧式。案例条目foo == bar必须是评估的编译时间,如果它恰好为false,则switch语句将导致错误。编译器也会将其减少为“无”。

{ 
  bool x=false; 
  switch (x) {
  case foo == bar:
    break;
  case false:
    // Compile time test that foo == bar
    break;
}

答案 5 :(得分:2)

你可以这样定义自己的静态断言:

#include <iostream>
template <bool b> class ClassStaticAssert;
template <>
class ClassStaticAssert<true>{static const bool value = true;};
#define STATIC_ASSERT(e) (ClassStaticAssert<e>())
int main()
{
    STATIC_ASSERT(0);
    return 0;
}

答案 6 :(得分:1)

与iammillind的解决方案类似,不幸的是,它只在运行时有用:

template <int A, int B>
class VALUES { 
};

// specialization to provide safe passage for equal values        
template <int X>
class VALUES<X, X> {
public:
   static void MY_VALUES_ARE_EQUAL() {}
};


#define ASSERT_EQUALITY(a, b)    \
{    \
   typedef VALUES<a, b> COMPILE_TIME_ASSERTION;       \
   COMPILE_TIME_ASSERTION::VALUES_ARE_EQUAL();     \
}

int main() {
   ASSERT_EQUALITY(1, 1);    // compiles just fine
   ASSERT_EQUALITY(1, 2);    // ERROR!
   // . . . 
 }

关于这一点的好处是它提供了一个很好的编译器消息。我的编译器说 我有以下几点:

  

'VALUES_ARE_EQUAL'不是'COMPILE_TIME_ASSERTION {aka VALUES&lt; 1,2&gt;}'的成员

您不需要typedef。没有:

  

'VALUES_ARE_EQUAL'不是'VALUES&lt; 1,2&gt;'的成员

当然,还有很多其他方法可以生成有用的消息。对于咯咯地笑:

// these give use some tips in the compiler warnings
class COMPILE_TIME_EQUALITY_ASSERTION {} compiler_message; 
class EQUAL_VALUES_ONLY_PLEASE {};


template <int A, int B>
class VALUES {
public:
   static void AreEqual(EQUAL_VALUES_ONLY_PLEASE) {}
};

template <int X>
class VALUES<X, X>
{
public:
   static void AreEqual(COMPILE_TIME_EQUALITY_ASSERTION) {}
};


#define ASSERT_EQUALITY(a, b)                                   \
{                                                               \
   VALUES<a, b>::AreEqual(compiler_message);                             \
}

int main() {
    ASSERT_EQUALITY(1, 1) // a-okay
    ASSERT_EQUALITY(1, 2) // ERROR!
}

我收到以下编译错误:

no matching function for call to:
‘VALUES<1,2>::AreEqual(COMPILE_TIME_EQUALITY_ASSERTION&)' 
candidate is:
static void VALUES<\A, B>::AreEqual(EQUAL_VALUES_ONLY_PLEASE) [with int A = 1, int B = 2]

静态成员函数/构造函数/字段赋值/隐私和模板规范的组合可以产生可能更适合您的情况的不同结果。

答案 7 :(得分:1)

template <int a, int b>
inline void static_assert_equal()
{
    typedef char enum_values_must_be_equal[a == b ? 1 : -1];
    (void) sizeof(enum_values_must_be_equal);
}

int main()
{
    enum { foo = 1, bar = 2, fum = foo };
    static_assert_equal<foo, fum>(); // compiles ok
    static_assert_equal<foo, bar>(); // fails at compile time
    return 0;
}

这源于checked_delete成语。

答案 8 :(得分:0)

你可以做一些预处理器魔术,比如

#define FOO_VALUE 263
#define BAR_VALUE 264

enum {foo=FOO_VALUE, bar=BAR_VALUE}

#if !(FOO_VALUE == BAR_VALUE)
#error "Not equal"
#endif

答案 9 :(得分:0)

我会选择一个可用的static_asserts。

  • 升压:: static_assert
  • C ++ 0x static_assert

但仅仅因为我在写这篇文章之前从未尝试过:

enum { foo=263, bar=264 };

template<bool test>
struct CompileAssert
{
    bool assert() {}
};

template<>
struct CompileAssert<false>  {}; // fail on false.

int main()
{
    CompileAssert<foo != bar>().assert();  // Now I have seen Chad above I like his static 
    CompileAssert<foo == bar>().assert();  // method better than using a normal method.
}                                          // But I tried zero length arrays first did 
                                           // not seem to work

答案 10 :(得分:0)

我建议看一下Eigen库的静态断言机制:

http://eigen.tuxfamily.org/dox/StaticAssert_8h_source.html

答案 11 :(得分:-1)

template<int X, int Y>
struct Check
{
  enum { value = false };
};

template<int X>
struct Check<X,X>
{
  enum { value = true };
};

我以int为例。您可以根据需要进行更改。 Here is the demo。 用法:

Check<foo, bar>::value