C++17 の variant と union の比較2020年05月06日 12時30分36秒

C++17 の variant の導入は typedef で見づらい部分がある欠点から始めてしまったが、variant 型自体はとても便利。variant は union をオブジェクト指向化したものと紹介したが、任意の型で自在に選択肢を作れる点でとても融通が効く。

まずは、variant 型と union 型の比較。

#include <variant>
#include <string>
#include <iostream>

int main()
{
    std::variant< int, std::string > number1;
    number1 = 3;
    number1 = 3.14;

    union number
    {
        int i;
        double d;
    } number2;

    number2.i = 3;
    number2.d = 3.14;
variant がたはテンプレートで受け付ける型を羅列していく。そのため、変数自体がどの型として処理をするかが推測される。
% c++ -std=c++17 variant_number.cpp
それに引き替えると、union 型は、型を指定して、そのアクセス方法も指定する。その為、variant 型に比べると冗長になってしまう。アクセス方法に名前を付けるので、同じ型を違う名前で指定できるのは、逆の立場としての利点には成り得る。

また、union は基本型しか指定できなかった。コンストラクタのある C++ オブジェクトはどの型が取られるかが決定できない。

#include <variant>
#include <string>

int main()
{
    std::variant< int, std::string > something;
    something = 3;
    something = "hello";

    union int_str
    {
        int i;
        std::string s;
    } legacy;

    legacy.i = 3;
    legacy.s = "hello";
}
% c++ -std=c++17 variant_int_string.cpp
variant_int_string.cpp:14:7: error: call to implicitly-deleted default
      constructor of 'union int_str'
    } legacy;
      ^
variant_int_string.cpp:13:14: note: default constructor of 'int_str' is
      implicitly deleted because variant field 's' has a non-trivial default
      constructor
        std::string s;
                    ^
1 error generated.
それに引き替え、variant は型指定なので、C++ のオブジェクトが問題なく使えるようになっている。未初期化の場合は、一番最初の型がデフォルトになる。この例の場合、string 型を union に入れた部分のみがエラーになっている。

前回次回