boost::variant
使用していますか、それともC ++プロジェクトで「タグ付きユニオン」またはバリアントタイプの多くのオープンソースC ++ 11の実装を使用していますか?
boost::variant
素晴らしいライブラリです。 boost::variant
についてのいくつかのことに対処するために、 strict_variant
作成しました。
TL; DRバージョンは、 boost::variant
またはstd::variant
とは異なり、 strict_variant
例外をスローしたり、動きを投げるタイプをサポートする努力で動的割り当てを行うことはないということです。デフォルトのバージョンは、これが発生する場合、単に静的アサートに失敗します。 strict_variant::easy_variant
この状況で割り当てを行います。そのため、必要に応じてオプトインできます。この種のことは、多くの場合、リアルタイムの要件を備えたプロジェクト、または埋め込まれたデバイスで大きな関心事であることが多く、これらのC ++機能が許可されていないか、単にこれらのC ++機能がない場合があります。 boost::variant
から来るが、制限的な要件を備えたプロジェクトでも使用される可能性のある「従来の」プロジェクトで使用される可能性のあるライブラリを作成している場合、 variant
タイプを使用する必要があります。 APIの一環として、 strict_variant
誰もが幸せに保つ方法を提供するかもしれません。
これに加えて、バリアントのインターフェイスには、日々の私見を使用する方が快適になるように対処されたいくつかの問題があります。 (これらは実際にはプロジェクトの元の動機でした。)
私はこのコードが警告やエラーメッセージなしでコンパイルするのが好きではありませんでした:
boost::variant<std::string, int > v;
v = true ;
私は通常、私のvariant
暗黙の変換が起こる可能性があることについてより制限的であることをむしろむしろむしろむしろむしろむしろむしろむしろむしろ
過負荷の解像度が曖昧であっても、このようなことがコンパイルされ、理にかなっていることをすることを望んでいました。
variant< bool , long , double , std::string> v;
v = true ; // selects bool
v = 10 ; // selects long
v = 20 . 5f ; // selects double
v = " foo " ; // selects string
また、そのような動作(そのような場合に選択されるもの)がポータブルであることを望んでいました。
( boost::variant
に不幸な動作があるこのようなコードの例については、ドキュメントの「抽象と動機付け」を参照してください。)
strict_variant
では、一部の候補者を削除することにより、これらの状況で過負荷解像度を変更します。
例えば:
bool
、インテグラル、フローティングポイント、ポインター、キャラクター、およびその他のクラスの間の標準変換を禁止しています。int -> long
and int -> long long
が候補である場合、 long long
排除されます。詳細については、ドキュメントを参照してください。
私はそのboost::variant
私のオブジェクトのバックアップコピーを静かに作成するのが好きではありませんでした。たとえば、すべてのCTORコールとDTOR呼び出しを記録するためにA
とB
定義されているこの単純なプログラムを検討してください。
int main () {
using var_t = boost::variant<A, B>;
var_t v{ A ()};
std::cout << " 1 " << std::endl;
v = B ();
std::cout << " 2 " << std::endl;
v = A ();
std::cout << " 3 " << std::endl;
}
boost::variant
次の出力を生成します。
A ()
A(A&&)
~A()
1
B()
B(B&&)
A( const A &)
~A()
B( const B &)
~A()
~B()
~B()
2
A()
A(A&&)
B( const B &)
~B()
A( const A &)
~B()
~A()
~A()
3
~A()
これは、一部のプログラマーにとってはかなり驚くべきことかもしれません。
対照的に、C ++ 17 std::variant
を使用している場合、または「時々空白のある」セマンティクスを持つバリアントの1つを使用すると、このようなものを取得します( std::experimental::variant
からの出力)
A ()
A(A&&)
~A()
1
B()
~A()
B(B&&)
~B()
2
A()
~B()
A(A&&)
~A()
3
~A()
これは、Naiveプログラマーがboost::variant
の内部詳細について知らない人が期待していることにはるかに近いものです。彼のオブジェクトのコピーは、ソースコードで見ることができるものです。
この種のことは通常重要ではありませんが、たとえば、厄介なメモリの破損の問題をデバッグしている場合(おそらくバリアントに含まれるオブジェクトの1つに悪いコードがある)、これらの余分なオブジェクト、動き、コピー、偶然にも複雑になります。
strict_variant
で得られるものは次のとおりです。
A ()
A(A&&)
~A()
1
B()
B(B&&)
~A()
~B()
2
A()
A(A&&)
~B()
~A()
3
~A()
しかし、 strict_variant
は空の状態がなく、完全に例外です!
( gcc 5.4
のこれらの例、 example
フォルダーのコードを参照してください。)
違いを要約するには:
std::variant
めったに空ではなく、常にスタックベースです。実際、例外がスローされたときに正確に空です。後で、空のときに訪問しようとすると、異なる例外がスローされます。boost::variant
決して空ではなく、通常はスタックベースです。例外をスローできるときはいつでも、動的割り当てとバックアップコピーを作成する必要がありますが、例外が実際にスローされない場合に解放されます。strict_variant
は決して空ではなく、現在のValueタイプが動きになっているときに正確にスタックベースになります。バックアップの動きやコピーを行うことはなく、例外をスローすることもありません。各アプローチにはメリットがあります。 strict_variant
アプローチを選択しました。これは、より簡単だと思うため、 boost::variant
およびstd::variant
の欠点と思われるものを回避します。そして、すべてのタイプのスルー移動を構築可能にすることができた場合、私はしばしば私ができると思います。その後、 strict_variant
、空の状態なしでstd::variant
と同じである最適なパフォーマンスを提供します。
デザインの詳細な説明については、ドキュメントをご覧ください。
バリアントの穏やかなイントロと厳格な変数の概要については、これについて私が示した講演のスライドを参照してください:[PPTX] [PDF]
githubページ。
strict_variant
C ++ 11標準をターゲットにします。
gcc >= 4.8
およびclang >= 3.5
で動作することが知られており、 MSVC 2015
に対してテストされています。
strict_variant
、 -fno-exceptions
と-fno-rtti
必要とするプロジェクトで使用できます。
Strictバリアントは、 Boost Softwareライセンスの下で利用できます。