static_assertは何をするもので、何に使用しますか?
-
22-07-2019 - |
質問
static_assert(...) 'C ++ 0x'
が手元の問題をエレガントに解決する例はありますか?
ランタイム assert(...)
に精通しています。通常の assert(...)
よりも static_assert(...)
を好むのはいつですか?
また、 boost
には BOOST_STATIC_ASSERT
と呼ばれるものがあります。これは static_assert(...)
と同じですか?
解決
頭の上から...
#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 ++ライブラリで予想される)ではなく、静的constとして宣言されていると仮定します。
実際に SomeLibrary
とコードをコンパイルし、すべてをリンクし、実行可能ファイルのみを then 実行する必要があるのとは対照的に、 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 :("
他のヒント
静的アサーションは、コンパイル時にアサーションを作成するために使用されます。静的アサーションが失敗すると、プログラムはコンパイルされません。これは、たとえば、正確に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
を使用します。
これを使用して、コンパイラの動作、ヘッダー、ライブラリ、さらには自分のコードに関する仮定が正しいことを確認します。たとえば、ここでは、構造体が予想されるサイズに正しくパックされていることを確認します。
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
を優先する必要があります。これがではない例には、パラメータと戻りコードのチェックが含まれます。
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 ... */
};
上記の条件のいずれかが満たされない場合、コンパイル時エラーが発生します。
static_assert
の用途の1つは、構造(ネットワークやファイルなどの外部とのインターフェース)が期待どおりのサイズになるようにすることです。これは、誰かが結果を認識せずに構造からメンバーを追加または変更する場合をキャッチします。 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");