static_assert(...)
('C++11') が手の問題をエレガントに解決する例を教えてください。
私はランタイムに精通していassert(...)
ます。static_assert(...)
通常よりも優先するのはいつassert(...)
ですか?
また、 にboost
というものがありますがBOOST_STATIC_ASSERT
、 と同じstatic_assert(...)
ですか?
static_assert(...)
('C++11') が手の問題をエレガントに解決する例を教えてください。
私はランタイムに精通していassert(...)
ます。static_assert(...)
通常よりも優先するのはいつassert(...)
ですか?
また、 にboost
というものがありますがBOOST_STATIC_ASSERT
、 と同じstatic_assert(...)
ですか?
静的アサートは、コンパイル時にアサーションを作成するために使用されます。静的アサーションが失敗すると、プログラムはコンパイルされません。これは、さまざまな状況で役立ちます。たとえば、unsigned int
正確に 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
。
私の頭の上から...
#include "SomeLibrary.h"
static_assert(SomeLibrary::Version > 2,
"Old versions of SomeLibrary are missing the foo functionality. Cannot proceed!");
class UsingSomeLibrary {
// ...
};
SomeLibrary::Version
それが d ではなく static const として宣言されていると仮定します#define
(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: エラー: 静的アサーションが失敗しました: 「Foo::bar が小さすぎます :(」
私はそれを使用して、コンパイラの動作、ヘッダー、ライブラリ、さらには自分のコードについての私の仮定が正しいことを確認します。たとえば、ここでは、構造体が予想されるサイズに正しくパックされていることを確認します。
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);
上記の例のように、動作が実行時ではなくコンパイル時に定義される場合よりも優先する必要がstatic_assert
あります。assert
これが当てはまらない例には、パラメーターと戻りコードのチェックが含まれます。
BOOST_STATIC_ASSERT
条件が満たされない場合に不正なコードを生成する C++0x 以前のマクロです。static_assert
標準化されており、より優れたコンパイラ診断を提供する可能性がありますが、意図は同じです。
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 ... */
};
上記の条件のいずれかが満たされない場合、コンパイル時エラーが発生します。
の用途の 1 つはstatic_assert
、構造 (ネットワークやファイルなどの外界とのインターフェイス) が期待どおりのサイズであることを確認することです。これにより、誰かが結果を認識せずに構造からメンバーを追加または変更するケースがキャッチされます。はそれstatic_assert
を拾い、ユーザーに警告します。
概念がない場合、static_assert
たとえばテンプレートで、単純で読みやすいコンパイル時の型チェックに使用できます。
template <class T>
void MyFunc(T value)
{
static_assert(std::is_base_of<MyBase, T>::value,
"T must be derived from MyBase");
// ...
}
これは元の質問に直接答えるものではありませんが、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");