Boost C++ 库

...世界上最受尊敬和专业设计的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

PrevUpHomeNext

第 35 章。Boost.StaticAssert

John Maddock

Steve Cleary

根据 Boost 软件许可协议 1.0 版分发。(参见随附文件 LICENSE_1_0.txt 或在 https://boost.ac.cn/LICENSE_1_0.txt 复制)

目录

概述和教程
在命名空间范围使用。
在函数范围使用
在类范围使用
在模板中使用
工作原理
测试程序

本手册也提供 打印友好的 PDF 格式

概述和教程

头文件 <boost/static_assert.hpp> 提供了两个宏

BOOST_STATIC_ASSERT(x)
BOOST_STATIC_ASSERT_MSG(x, msg)

如果 integral-constant-expression x 不为真,两者都会生成编译时错误消息。换句话说,它们是 assert 宏的编译时等效物;这有时被称为“编译时断言”,但在本文档中将称为“静态断言”。请注意,如果条件为 true,则宏既不会生成代码也不会生成数据 - 并且宏也可以在命名空间、类或函数范围中使用。当在模板中使用时,静态断言将在模板实例化时进行评估;这对于验证模板参数特别有用。

如果 C++0x 的 static_assert 功能可用,则两个宏都将使用它。对于 BOOST_STATIC_ASSERT(x),错误消息将是 x 的字符串化版本。对于 BOOST_STATIC_ASSERT_MSG(x, msg),错误消息将是 msg 字符串。

如果 C++0x 的 static_assert 功能不可用,BOOST_STATIC_ASSERT_MSG(x, msg) 将被视为 BOOST_STATIC_ASSERT(x)

以下内容假设 C++0x 的 static_assert 功能不可用。

BOOST_STATIC_ASSERT 的目标之一是生成可读的错误消息。这些消息立即告诉用户,库的使用方式不受支持。虽然错误消息显然因编译器而异,但您应该看到类似这样的内容

Illegal use of STATIC_ASSERTION_FAILURE<false>

旨在至少能引起注意!

您可以在可以放置声明的任何位置使用 BOOST_STATIC_ASSERT,即在类、函数或命名空间范围,以下示例对此进行了说明

在命名空间范围使用。

如果存在必须始终为真的要求,则可以在命名空间范围中使用该宏;通常这意味着某些平台特定的要求。假设我们要求 int 至少为 32 位整型,并且 wchar_t 为无符号类型。我们可以按如下方式在编译时验证这一点

#include <climits>
#include <cwchar>
#include <limits>
#include <boost/static_assert.hpp>

namespace my_conditions {

   BOOST_STATIC_ASSERT(std::numeric_limits<int>::digits >= 32);
   BOOST_STATIC_ASSERT(WCHAR_MIN >= 0);

} // namespace my_conditions

此处使用命名空间 my_conditions 需要一些注释。宏 BOOST_STATIC_ASSERT 的工作方式是生成 typedef 声明,并且由于 typedef 必须有一个名称,因此宏通过使用 __LINE__ 的值来修改存根名称来自动生成一个名称。当在类或函数范围中使用 BOOST_STATIC_ASSERT 时,保证每次使用 BOOST_STATIC_ASSERT 都会生成一个在该范围内唯一的名称(前提是您每行只使用一次宏)。但是,当在头文件的命名空间范围中使用时,该命名空间可以跨多个头文件继续,每个头文件可能都有自己的静态断言,并且在“同一”行上,从而生成重复的声明。从理论上讲,编译器应该静默地忽略重复的 typedef 声明,但是许多编译器并没有这样做(即使这样做,它们也有权在这种情况下发出警告)。为了避免潜在的问题,如果您在头文件中并在命名空间范围中使用 BOOST_STATIC_ASSERT,请将它们括在对该头文件唯一的命名空间中。

在函数范围使用

当需要检查模板参数时,该宏通常在模板函数内的函数范围中使用。想象一下,我们有一个基于迭代器的算法,需要随机访问迭代器。如果使用不符合我们要求的迭代器实例化该算法,则最终会生成错误,但这可能嵌套在多个模板的深处,使用户难以确定哪里出了问题。一种选择是在模板的顶层添加静态断言,在这种情况下,如果条件不满足,则会以一种使用户相当容易地意识到模板被误用的方式生成错误。

#include <iterator>
#include <boost/static_assert.hpp>
#include <boost/type_traits.hpp>

template <class RandomAccessIterator >
RandomAccessIterator foo(RandomAccessIterator from,
                         RandomAccessIterator to)
{
   // this template can only be used with
   // random access iterators...
   typedef typename std::iterator_traits<
         RandomAccessIterator >::iterator_category cat;
   BOOST_STATIC_ASSERT(
      (boost::is_convertible<
         cat,
         const std::random_access_iterator_tag&>::value));
   //
   // detail goes here...
   return from;
}

这里有几个脚注需要说明:assert 周围的额外括号是为了防止预处理器将 is_convertible 模板内的逗号解释为宏参数分隔符;is_convertible 的目标类型是引用类型,因为某些编译器在使用 is_convertible 时,当转换是通过用户定义的构造函数进行的,会出现问题(在任何情况下,都不能保证迭代器标签类是可复制构造的)。

在类范围使用

该宏通常在作为模板的类内部使用。假设我们有一个模板类,它需要一个至少具有 16 位精度的无符号整型作为模板参数,我们可以使用类似这样的方法来实现这一点

#include <limits>
#include <boost/static_assert.hpp>

template <class UnsignedInt>
class myclass
{
private:
   BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::is_specialized, "myclass can only be specialized for types with numeric_limits support.");
   BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::digits >= 16, "Template argument UnsignedInt must have at least 16 bits precision.")
   BOOST_STATIC_ASSERT_MSG(std::numeric_limits<UnsignedInt>::is_integer, "Template argument UnsignedInt must be an integer.");
   BOOST_STATIC_ASSERT_MSG(!std::numeric_limits<UnsignedInt>::is_signed, "Template argument UnsignedInt must not be signed.");
public:
   /* details here */
};

在模板中使用

通常,当在类或函数模板内部使用静态断言时,只有在实例化使用它的模板时才会实例化。但是,有一个潜在的问题需要注意:如果静态断言不依赖于一个或多个模板参数,则编译器允许在第一次看到静态断言时对其进行评估,而不管模板是否被实例化,例如

template <class T>
struct must_not_be_instantiated
{
   BOOST_STATIC_ASSERT(false);
};

在某些编译器(例如 Intel 8.1 或 gcc 3.4)上会产生编译器错误,而不管模板是否被实例化。在这种情况下,一种解决方法是强制断言依赖于模板参数

template <class T>
struct must_not_be_instantiated
{
   // this will be triggered if this type is instantiated
   BOOST_STATIC_ASSERT(sizeof(T) == 0);
};

PrevUpHomeNext