web-dev-qa-db-ja.com

static_assertは何をし、何に使用しますか?

static_assert(...) 'C++0x'が手元の問題をエレガントに解決する例を挙げていただけますか?

ランタイムassert(...)に精通しています。通常のstatic_assert(...)よりもassert(...)を好むのはいつですか?

また、boostには_BOOST_STATIC_ASSERT_と呼ばれるものがありますが、static_assert(...)と同じですか?

105
AraK

頭の上から...

#include "SomeLibrary.h"

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

class UsingSomeLibrary {
   // ...
};

SomeLibrary::Version#defined(C++ライブラリで予想される)ではなく、静的なconstとして宣言されていると仮定します。

SomeLibraryとコードを実際にコンパイルし、すべてをリンクし、実行可能ファイルのみを実行するのとは対照的に、thenは、SomeLibraryの互換性のないバージョンのコンパイルに30分かかったことを確認します。

@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:エラー:静的アサーションに失敗しました: "Foo :: bar is too small :(" 
68
Mark Rushakoff

静的アサートは、コンパイル時にアサーションを作成するために使用されます。静的アサーションが失敗すると、プログラムはコンパイルされません。これは、たとえば、正確に32ビットのunsigned intオブジェクトに厳密に依存するコードによっていくつかの機能を実装する場合など、さまざまな状況で役立ちます。このような静的アサートを配置できます

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を使用します。

120
AnT

これを使用して、コンパイラの動作、ヘッダー、ライブラリ、さらには自分のコードについての仮定が正しいことを確認します。たとえば、ここでは、構造体が予想されるサイズに正しくパックされていることを確認します。

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

_stdio.h_のfseek()をラップするクラスで、_enum Origin_を使用していくつかのショートカットを作成し、それらのショートカットが_stdio.h_で定義された定数と一致することを確認しました

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

上記の例のように、実行時ではなくコンパイル時に動作が定義されている場合は、assertよりも_static_assert_を優先する必要があります。これがnotの例には、パラメーターと戻りコードのチェックが含まれます。

_BOOST_STATIC_ASSERT_は、条件が満たされない場合に不正なコードを生成するC++ 0xより前のマクロです。 _static_assert_は標準化されていますが、コンパイラの診断が改善される可能性がありますが、意図は同じです。

12
Matt Joiner

BOOST_STATIC_ASSERTは、static_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 ... */
};

上記の条件のいずれかが満たされない場合、これによりコンパイル時エラーが発生します。

9
nurettin

static_assertの使用法の1つは、構造(ネットワークやファイルなどの外部とのインターフェース)が期待どおりのサイズになるようにすることです。これにより、結果を認識せずに誰かが構造からメンバーを追加または変更した場合にキャッチされます。 static_assertはそれを選択し、ユーザーに警告します。

7
Greg Hewgill

概念がなければ、static_assert単純で読みやすいコンパイル時の型チェック用、たとえばテンプレート内:

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

// ...
}
3
vladon

これは元の質問に直接答えるものではありませんが、C++ 11より前のこれらのコンパイル時チェックを実施する方法について興味深い研究を行っています。

Andrei Alexanderscuによる Modern C++ Design の第2章(セクション2.1)は、このようなコンパイル時アサーションのこのアイデアを実装しています

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
nightlytrails