std::variant は、C++17 で導入された union の一種です。std::variant は、異なる型のデータを 1 つの変数に格納し、型の安全性を保持することができます。これは従来の union では提供できない機能です。
主な特徴#
型の安全性#
std::variant を使用すると、常に正しい型の値にアクセスできることが保証されます。間違った型にアクセスすると、std::bad_variant_access 例外がスローされます。
自動管理#
std::variant と std::any は、内部データの作成と破棄を管理し、正しいリソース管理を保証します。
アクセス制御#
std::get を使用して、std::variant に格納されたデータにアクセスできます(型が正しいことを確認する必要があります)。
visit との組み合わせが可能#
std::variant に格納されたデータに対して std::visit を使用して操作することができます。
サンプル#
#include <variant>
#include <iostream>
#include <string>
int main() {
std::variant<int, float, std::string> v;
v = 20;
std::cout << std::get<int>(v) << std::endl; // 出力: 20
v = 3.14f;
std::cout << std::get<float>(v) << std::endl; // 出力: 3.14
v = "Hello, world";
std::cout << std::get<std::string>(v) << std::endl; // 出力: Hello, world
// 安全なアクセス
try {
std::cout << std::get<float>(v) << std::endl; // floatを取得しようとしますが、現在はstd::stringなので例外がスローされます
} catch (const std::bad_variant_access& e) {
std::cout << e.what() << std::endl; // 例外情報を出力
}
// visitの使用
std::visit([](auto&& arg) {
std::cout << arg << std::endl;
}, v); // 現在の値を安全に出力します。ここでは "Hello, world" です
return 0;
}
この例では、std::variant は int、float、std::string を格納するために使用されています。std::get を使用して、std::variant に格納されたデータに安全にアクセスする方法や、std::visit を使用して関数や訪問者を格納された値に適用する方法が示されています。
実際の環境#
inline ResourceVariant ResourceManager::flipResource(ResourceVariant resource)
{
return std::visit([](auto&& res) -> ResourceVariant {
using T = std::decay_t<decltype(res)>;
if constexpr (std::is_same_v<T, IMAGE>) {
IMAGE flippedImg;
flipImage(&res, &flippedImg);
return flippedImg;
}
else if constexpr (std::is_same_v<T, Atlas>) {
Atlas flippedAtla;
flipAtlas(res, flippedAtla);
return flippedAtla;
}
}, resource);
}
このコードは、異なるタイプのリソースを処理するために std::variant と std::visit を使用する方法を示しています。ここでは、ResourceManager::flipResource メソッドが、ResourceVariant 型の resource という名前のパラメータを受け取り、同じ型の ResourceVariant を返します。この ResourceVariant は std::variant 型であり、IMAGE や Atlas などの異なるリソースタイプを含むことができます。