Boost C++ Libraries Home Libraries People FAQ More

PrevUpHomeNext

Chapter 17. Boost.StaticAssert

John Maddock

Steve Cleary

Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt )

Table of Contents 目錄

綜述及指南
在名字空間域中使用
在函數域中使用
在類域中使用
在模板中使用
它是如何工作的
測試程序

本手冊還提供了 適於打印的 PDF 格式

綜述及指南

頭文件 <boost/static_assert.hpp> 提供了一個獨立的宏 BOOST_STATIC_ASSERT(x),如果 integral-constant-expression x 不為 true,將產生一個編譯時錯誤信息。換句話說,它是一個 assert 宏在編譯時的等價物,這有時被稱為「編譯時斷言」,但在當前這些文檔中被稱為「靜態斷言」。注意,如果條件為 true,這個宏將不生成代碼和數據——而這個宏既可以用於名字空間域,類域,也可以用於函數域。當用在模板中時,靜態斷言在模板實例化時被評估,這在確定模板參數時特別有用。

BOOST_STATIC_ASSERT 的目標之一是生成可讀的錯誤信息。這些就立刻告訴使用者一個庫的某種用法是不被支持的。這時的錯誤信息針對不同的編譯器會有明顯的不同,但你應該會看到類似這樣的東西:

Illegal use of STATIC_ASSERTION_FAILURE<false>

這樣至少能讓人抓住要害!

你可以在任何能夠放置一個聲明的地方使用 BOOST_STATIC_ASSERT,也就是說在類域,函數與或名字空間域中,以下是舉例說明:

在名字空間域中使用

如果在名字空間域中有一些條件必須總為 true(這通常意味著平台的特殊需求),那麼就可以把這個宏用在名字空間域中。假設我們需要 int 至少是一個 32-bit 的整類型,而且 wchar_t 必須是一個 unsigned 類型。我們可以在編譯時進行這樣的校驗:

#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 時會出問題(在某些情況下,不能保證這裡的 iterator tag 類是可拷貝構造的)。

在類域中使用

這個宏經常會用在那些是模板的類中。假設我們有一個模板類需要一個精度至少為 16-bit 的無符號整類型作為一個模板參數,我們可以像這樣做到這一點:

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

template <class UnsignedInt>
class myclass
{
private:
   BOOST_STATIC_ASSERT((std::numeric_limits<UnsignedInt>::digits >= 16)
                        && std::numeric_limits<UnsignedInt>::is_specialized
                        && std::numeric_limits<UnsignedInt>::is_integer
                        && !std::numeric_limits<UnsignedInt>::is_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);
};

Last revised: July 13, 2008 at 17:08:33 -0400


PrevUpHomeNext