2009-10-30 84 views
72

你可以舉一個例子,其中static_assert(...) 'C++0x'將優雅地解決手中的問題?static_assert是做什麼的,你會用它做什麼?

我熟悉運行時間assert(...)。我應該在什麼時候比普通assert(...)更喜歡static_assert(...)

此外,在boost有一種叫做BOOST_STATIC_ASSERT,是否與static_assert(...)相同?

+0

還請參見:BOOST_MPL_ASSERT,BOOST_MPL_ASSERT_NOT,BOOST_MPL_ASSERT_MSG,BOOST_MPL_ASSERT_RELATION [http://www.boost.org/doc/libs/1_40_0/libs/mpl/doc/refmanual/asserts.html]以獲取更多選項。 _MSG特別好,一旦你找出如何使用它。 – KitsuneYMG

回答

50

關閉我的頭頂......

#include "SomeLibrary.h" 

static_assert(SomeLibrary::Version > 2, 
     "Old versions of SomeLibrary are missing the foo functionality. Cannot proceed!"); 

class UsingSomeLibrary { 
    // ... 
}; 

假設SomeLibrary::Version被聲明爲靜態常量,而不是#define d(正如人們所期望的C++庫)。

對比具有實際編譯SomeLibrary和你的代碼,鏈接的一切,並運行可執行文件只然後地發現,你花30分鐘編譯SomeLibrary不兼容的版本。

@Arak,以響應您的評論:是的,你可以有static_assert只是坐在了何地,從它的外觀:

class Foo 
{ 
    public: 
     static const int bar = 3; 
}; 

static_assert(Foo::bar > 4, "Foo::bar is too small :("); 

int main() 
{ 
    return Foo::bar; 
} 
 
$ g++ --std=c++0x a.cpp 
a.cpp:7: error: static assertion failed: "Foo::bar is too small :(" 
+1

我有點困惑,你可以把'static_assert'放在非執行上下文中嗎?這似乎是一個很好的例子:) – AraK

+2

是的,靜態斷言,因爲他們站在通常實現創建一個對象,只有當謂詞是真實的只有定義。這隻會成爲一個全球性的。 – GManNickG

+0

我不確定這是否符合回答原始問題的完整性,但很好的示範 –

3

static_assert的一種用法可能是確保結構(即與外部世界的接口,如網絡或文件)的大小與您的預期完全相同。這將會發現某些人在沒有意識到結果的情況下增加或修改結構中的成員的情況。 static_assert會提取並警告用戶。

10

我用它來保證我對編譯器的行爲的假設,標題,庫,甚至我自己的代碼都是正確的。例如,在這裏我驗證結構已被正確打包到預期的大小。

struct LogicalBlockAddress 
{ 
#pragma pack(push, 1) 
    Uint32 logicalBlockNumber; 
    Uint16 partitionReferenceNumber; 
#pragma pack(pop) 
}; 
BOOST_STATIC_ASSERT(sizeof(LogicalBlockAddress) == 6); 

在一類包裝stdio.hfseek(),我已經採取了一些快捷方式enum Origin並檢查這些快捷鍵與stdio.h

uint64_t BasicFile::seek(int64_t offset, enum Origin origin) 
{ 
    BOOST_STATIC_ASSERT(SEEK_SET == Origin::SET); 

定義的常量對準你應該更喜歡static_assertassert當行爲是在編譯時定義的,而不是在運行時定義的,比如我上面給出的例子。例如,這是而不是這種情況將包括參數和返回碼檢查。

BOOST_STATIC_ASSERT是一個預C++ 0x宏,如果條件不滿足,會生成非法代碼。意圖是相同的,儘管static_assert是標準化的,並且可以提供更好的編譯器診斷。

96

靜態斷言用於在編譯時斷言。當靜態斷言失敗時,程序不會編譯。這在不同情況下非常有用,例如,如果您通過嚴格取決於具有完全32位的對象的代碼來實現某些功能。你可以把這樣的靜態斷言

static_assert(sizeof(unsigned int) * CHAR_BIT == 32); 

在你的代碼。在另一個平臺上,使用不同大小的unsigned int類型,編譯將失敗,從而引起開發人員注意代碼中有問題的部分,並建議他們重新實施或重新檢查它。

再例如,您可能希望通過一些積分值作爲void *指向函數(一劈,但多次使用),並要確保積分值將融入指針

int i; 

static_assert(sizeof(void *) >= sizeof i); 
foo((void *) i); 

您可能需要在該char類型與負值簽署

static_assert(CHAR_MIN < 0); 

或整數分頻的資產將向零舍

static_assert(-5/2 == -2); 

依此類推。

許多情況下的運行時斷言可以用來代替靜態斷言,但運行時斷言僅在運行時纔有效,並且只有當控制權通過斷言時纔有效。由於這個原因,一個失敗的運行時斷言可能處於休眠狀態,在很長一段時間內未被檢測到。

當然,靜態斷言中的表達式必須是編譯時常量。它不能是運行時間值。對於運行時間值,您別無選擇,只能使用普通的assert

+0

這是一個很好的答案,我希望你知道你有我的+1 :) – AraK

+1

是不是static_assert必須有一個字符串文字作爲第二個參數? –

+0

@Trevor Hickey:是的。但我並沒有試圖從C++ 11中專門引用'static_assert'。我上面的'static_assert'只是靜態斷言的一些抽象實現。 (我個人在C代碼中使用類似的東西)。我的答案旨在說明靜態斷言的一般用途及其與運行時斷言的區別。 – AnT

9

BOOST_STATIC_ASSERTstatic_assert功能的跨平臺包裝。

目前,我正在使用static_assert爲了對類實施「概念」。

例如:

template <typename T, typename U> 
struct Type 
{ 
    BOOST_STATIC_ASSERT(boost::is_base_of<T, Interface>::value); 
    BOOST_STATIC_ASSERT(std::numeric_limits<U>::is_integer); 
    /* ... more code ... */ 
}; 

這將導致一個編譯時間錯誤,如果上述任何條件都不滿足。

+3

既然C++ 11已經出來(並且已經出來了一段時間),static_assert應該被所有主要編譯器的更新版本所支持。 對於那些不能等待C++ 14(希望包含模板約束)的人來說,這是一個非常有用的static_assert應用程序。 – Collin

2

這並不直接回答原始問題,但對如何在C++ 11之前執行這些編譯時檢查進行了有趣的研究。

第2章(第2.1節)的Modern C++ Design安德烈Alexanderscu實現))這一想法編譯時斷言的這樣

template<int> struct CompileTimeError; 
template<> struct CompileTimeError<true> {}; 

#define STATIC_CHECK(expr, msg) \ 
{ CompileTimeError<((expr) != 0)> ERROR_##msg; (void)ERROR_##msg; } 

比較宏STATIC_CHECK(和static_assert(

STATIC_CHECK(0, COMPILATION_FAILED); 
static_assert(0, "compilation failed"); 
2

在如果沒有概念,可以使用static_assert進行簡單和可讀的編譯時類型檢查,例如,在模板中:

template <class T> 
void MyFunc(T value) 
{ 
static_assert(std::is_base_of<MyBase, T>::value, 
       "T must be derived from MyBase"); 

// ... 
} 
相關問題