【问题标题】:Why prefer template-based static assert over typedef-based static assert?为什么更喜欢基于模板的静态断言而不是基于 typedef 的静态断言?
【发布时间】:2011-10-20 17:20:56
【问题描述】:

对于没有内置 static_assert 的 C++ 版本,有两个 widely used 静态断言实现。

第一个在Boost中使用,使用a template and a specialization of that template:

template <bool> struct static_assert;
template <> struct static_assert<true> {}; // only true is defined
#define  STATIC_ASSERT(x) static_assert<(x)>()

这里一旦要检查的条件为假,编译器将无法找到模板的通用版本并且编译失败。

第二个使用typedef:

#define STATIC_ASSERT( x ) typedef char __STATIC_ASSERT__[( x )?1:-1]

一旦违反了检查条件,编译器就会尝试 typedef 一个大小为 -1 的数组,这是非法的,因此会出现编译时错误。

对我来说,后者更好,因为它保证不发出任何代码,而且它可以像这样使用(来自here):

template<int Shift> class BinaryFlag {
    STATIC_ASSERT( 0 <= Shift && Shift < sizeof( DWORD) * CHAR_BIT );
    public:
    static const DWORD FlagValue = static_cast<DWORD>( 1 << Shift );
};
#define BINARY_FLAG( n ) CBinaryFlag<n>::FlagValue

而前者不能那样使用。

有什么理由更喜欢静态断言的前一个实现而不是后一个?

【问题讨论】:

  • +1,但我仍然不明白第一个版本将如何发出代码?这些只是空的struct 声明,对吗?
  • @iammilind:有一个临时结构可能会发出一些代码。
  • @sharptooth 类型为空 POD。我看不到任何不省略构造的情况。也就是说,您更喜欢第二种解决方案的另一点是有效的。
  • 选择其中一个的另一个原因可能是发生故障时生成的错误有多好。我猜在大多数平台上,即使在这方面,typedef 解决方案也会更好。
  • 也不能保证 typedef 不会发出任何代码。例如,允许为分析目的检测其代码的实现在此处发出一些代码,以增加当前源文件行号的记录。那将符合标准。如果您只是在谈论可视化 C++,那么对于每种断言,您都可以使用您感兴趣的编译器选项检查它是否发出代码,我怀疑您会看到启用任何优化的问题。

标签: c++ templates visual-c++ static-assert


【解决方案1】:

STATIC_ASSERT 的第二个版本你不能在同一个区块中一个接一个地使用。

template<int N, int M>
void foo ()
{
  STATIC_ASSERT(N<M), STATIC_ASSERT(M<0);  // error
};

Demo.

另一方面,在您发布的示例中,您不能使用第一个版本(因为它处理临时结构)。所以这两个版本都有自己的受众。我可以说第一个版本是编译和运行时的混合。但是第二个版本纯粹是编译时间。

编辑:有时为了可读性,您可能希望使用命令运算符将所有断言放在单行中(在这种情况下,只有最后一条指令有效)。我知道它们也可以与; 一起使用。但只是为了举例,这是一个用例。

同样会有一些情况,对象构造可以,但在语法上放置 typedef 就不行了。所以所有这些都会落到同一个地方。

【讨论】:

  • 虽然有一些简单的方法可以解决这个问题(并且这些方法用于成语的“真实”实现中)——例如,将 __LINE__ 数字附加到 typedef 的名称:@987654322 @
  • 这很有趣 - VC++ 9 编译没问题。
  • @iammilind 老实说,我在实践中没有看到问题。一行,一句话。这是一条如此简单的规则,没有理由违反它。
  • @sharptooth 我相信 VC++9 是正确的。据我所知,这只会在 typedef 为不同类型时失败。在这种情况下,除非触发断言,否则永远不会发生这种情况。
【解决方案2】:

我通常在我自己的代码中使用第二种或它的一些变体。 在实践中,Boost 变体的优点是可以使用 表达式可能出现的任何地方,而不仅仅是在语句级别。它 缺点是只能在表达式可能的地方使用 出现,因此不在命名空间范围内。

【讨论】:

    猜你喜欢
    • 2017-06-20
    • 2015-01-04
    • 1970-01-01
    • 2011-08-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-24
    • 1970-01-01
    相关资源
    最近更新 更多