---
# System prepended metadata

title: 'std::variant'
tags: [elkurin-daily-note, C++-General]

---

# std::variant

C++17で導入された[std::variant](https://cpprefjp.github.io/reference/variant/variant.html)を見かけたので確認する。

## overview
variant型は格納されうる型のリストを指定して、その中に含まれるどれかの型に一致するオブジェクトを保持できるもの。  
使用例 (absl版だがstdと変わらない)：
```cpp=
absl::variant<gfx::OverlayTransform, gfx::Transform> transform =
  gfx::OVERLAY_TRANSFORM_NONE;
```
型マッチっぽいことができそう。

このvariant型の変数にはそのままgfx::OverlayTransformやgfx::Transform型を代入できる。
```cpp=
// A型代入
std::variant<A, B, C> v = A{}; 
// B型に変更
v = B{};
```

このstd::variantテンプレートは1つ以上のタイプたちがあれば受け付ける。  
また型は[std::destructible](https://cpprefjp.github.io/reference/concepts/destructible.html)である必要がある。

## 関連メソッド
[std::holds_alternative](https://cpprefjp.github.io/reference/variant/holds_alternative.html) は variant型が現在保持している型を確認できる。  
例：
```cpp=
if (absl::holds_alternative<gfx::OverlayTransform>(overlay->transform)) {
  ...;
}
```

`overlay`の中にはOverlayTranform型またはTransform型が候補として入っており、上記のコードでは型がOverlayTransformに一致しているか確認する。

[std::visit](https://cpprefjp.github.io/reference/variant/visit.html)は中に入っている型に関わらず共通のメソッドを呼び出せる。  
```cpp=
std::variant<T1, T2> v;
int result = std::visit([](auto& x) {
  x.GetResult();
}, v);
```


## unionと違う？
共用体と呼ばれる[union](https://en.cppreference.com/w/cpp/language/union)は以下のように宣言することで複数の型のうちいずれかをもつ型を定義できる。
```cpp=
union S {
  std::int32_t n;
  std::uint16_t s[2];
  std::uint8_t c;
};    
```
unionは古い仕様でvariantがC++17になってからは基本variantを推奨しているっぽい。
[std::variant](https://en.cppreference.com/w/cpp/utility/variant)より
> The class template std::variant represents a type-safe union. ... Consistent with the behavior of unions during aggregate initialization, a default-constructed variant holds a value of its first alternative, unless that alternative is not default-constructible (in which case the variant is not default-constructible either). 

型安全なunionとのこと。  

[where-to-use-stdvariant-over-union](https://stackoverflow.com/questions/42082328/where-to-use-stdvariant-over-union)に詳細。

そもそもvariantのほうが使いやすそう。  
unionでは現在activeなデータを`s.n`や`s.c`といった感じで読み取るが、それよりobjectにアクセスするだけで現状の方がわかり型マッチも1メソッドでできる方が可読性が高い。