Boost C++ 库

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

PrevUpHomeNext

第 37 章. 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)

如果整型常量表达式 x 不为 true,这两个宏都会生成编译时错误消息。换句话说,它们是 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;
}

这里需要说明几点:断言周围的额外括号是为了防止预处理器将 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