あなたのアプローチは基本的に正しい方向でした。入れるタイプを知る必要があります。何を入れるかを知っている限り、使用するboost::any
ことができ、ほぼ何でもマップに入れることができます。
std::map<std::string, boost::any> table;
table["hello"] = 10;
std::cout << boost::any_cast<int>(table["hello"]); // outputs 10
一部の回答では、boost::variant
この問題を解決するために を使用することが推奨されていました。ただし、マップに任意の型付きの値を格納することはできません (必要に応じて)。可能なタイプのセットを事前に知っておく必要があります。それを考えると、上記をより簡単に行うことができます:
typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = 10;
// outputs 10. we don't have to know the type last assigned to the variant
// but the variant keeps track of it internally.
std::cout << table["hello"];
その目的のためにboost::variant
オーバーロードするため、それは機能します。operator<<
バリアントに現在含まれているものを保存したい場合は、次のboost::any
場合と同様に、タイプを知る必要があることを理解することが重要です。
typedef boost::variant<int, std::string, void*> variant_type;
std::map<std::string, variant_type> table;
table["hello"] = "bar";
std::string value = boost::get<std::string>(table["hello"]);
バリアントへの割り当ての順序は、コードの制御フローの実行時のプロパティですが、使用される変数の型はコンパイル時に決定されます。したがって、バリアントから値を取得したい場合は、その型を知る必要があります。別の方法は、バリアントのドキュメントで概説されているように、訪問を使用することです。バリアントには、最後に割り当てられたタイプを示すコードが格納されているため、機能します。それに基づいて、使用するビジターのオーバーロードを実行時に決定します。boost::variant
は非常に大きく、完全に標準に準拠しているわけではありませんが、boost::any
標準に準拠していますが、小さな型でも動的メモリを使用します (したがって、低速です。バリアントは小さな型にスタックを使用できます)。したがって、使用するものをトレードオフする必要があります。
何かを行う方法だけが異なるオブジェクトを実際にそこに入れたい場合は、ポリモーフィズムがより良い方法です。派生元の基本クラスを持つことができます:
std::map< std::string, boost::shared_ptr<Base> > table;
table["hello"] = boost::shared_ptr<Base>(new Apple(...));
table["hello"]->print();
基本的にこのクラスレイアウトが必要になります:
class Base {
public:
virtual ~Base() { }
// derived classes implement this:
virtual void print() = 0;
};
class Apple : public Base {
public:
virtual void print() {
// print us out.
}
};
boost::shared_ptr
いわゆるスマートポインターです。オブジェクトをマップから削除すると、オブジェクトが自動的に削除され、他に何も参照されなくなります。理論的には、プレーン ポインターでも作業できますが、スマート ポインターを使用すると安全性が大幅に向上します。リンク先のshared_ptrマニュアルを読んでください。