はじめに
C++は静的型付け言語であり、通常、一つの変数には一つの決まった型の値しか格納できません。しかし、時には「コンテナにint
もstring
も、自作クラスも、何でもかんでも格納したい」という場面があります。
C++17より前は、このような要求には void*
や共用体 (union
) が使われていましたが、型安全性が低く、危険なキャストが必要でした。
この問題を解決するのが std::any
です。std::any
は、あらゆる型の値を安全に保持できる型で、中身を取り出す際には、std::any_cast
を使って、厳密な型チェックの元で安全に値を取り出すことができます。
【前提】C++17とは?
C++17(シーピープラスいちなな)は、2017年に正式化されたC++言語の規格です。std::any
はこのC++17で追加された機能のため、利用するにはC++17に対応したコンパイラが必要です。
std::any
を使ったサンプルコード
このコードは、std::any
型の変数 data
に、int
, double
, std::string
と、次々に異なる型の値を代入し、その都度 any_cast
を使って値を取り出す様子を示します。
完成コード
#include <iostream>
#include <any> // std::any, std::any_cast
#include <string>
#include <typeinfo> // typeid
using namespace std;
int main() {
// 1. std::any 変数を宣言
any data;
// --- int値を代入 ---
data = 100;
// .type()で、現在保持している型を調べる
if (data.type() == typeid(int)) {
// 2. any_cast<T>で、指定した型Tとして値を取り出す
int n = any_cast<int>(data);
cout << "int値: " << n << endl;
}
// --- string値を代入 ---
data = string("こんにちは");
if (data.type() == typeid(string)) {
string s = any_cast<string>(data);
cout << "string値: " << s << endl;
}
// --- 不正なキャストを試みる ---
try {
// 現在はstringが入っているので、doubleへのキャストは失敗し、例外を投げる
double d = any_cast<double>(data);
}
catch (const bad_any_cast& e) {
cout << "エラー: " << e.what() << endl;
}
// --- 3. ポインタを使った安全なキャスト ---
// もしdataの中身がstring*でなければ、p_strはnullptrになる
if (string* p_str = any_cast<string>(&data)) {
cout << "ポインタ経由での値: " << *p_str << endl;
// ポインタ経由で元の値を変更
*p_str = "さようなら";
}
// 元の値が変わっていることを確認
cout << "変更後のstring値: " << any_cast<string>(data) << endl;
return 0;
}
コードの解説
1. any data;
と代入
std::any
型の変数を宣言します。この data
変数には、int
, double
, string
や自作クラスなど、コピー可能なあらゆる型の値を代入できます。代入すると、data
はその値のコピーを内部に保持します。
2. any_cast<T>(data)
any
に格納された値を取り出すには、std::any_cast
を使います。
any_cast<int>(data)
:<>
の中に、取り出したい型を指定します。- 型チェック:
any_cast
は、any
が現在保持している型と、指定された型が完全に一致するかをチェックします。- 一致する場合: その型の値を返します。
- 一致しない場合:
std::bad_any_cast
という例外を投げます。これにより、不正な型変換によるプログラムのクラッシュを防ぎます。
3. any_cast<string>(&data)
any_cast
の引数に any
オブジェクトへのポインタを渡すと、挙動が変わります。
- 戻り値: 指定した型へのポインタを返します。
- キャスト失敗時: 例外を投げる代わりに、
nullptr
を返します。
if
文と組み合わせることで、try-catch
ブロックなしで、より簡潔に安全なキャストを行うことができます。
まとめ
今回は、C++17で導入された std::any
を使って、型安全な方法であらゆる値を保持する方法を解説しました。
std::any
: あらゆる型の値を保持できるラッパー。any.type() == typeid(T)
: 現在の型を調べることができる。std::any_cast<T>(any)
: 型をチェックしながら、安全に値を取り出す。キャストに失敗すると例外を投げる。std::any_cast<T*>(&any)
: ポインタとして安全に値を取り出す。キャストに失敗するとnullptr
を返す。
std::any
は、異なる型のデータを一つのコレクションに格納したい場合など、静的な型システムの制約を安全に乗り越えるための強力なツールです。