Any型の実装
Anyを実装しよう
Any型とは
何でも格納できる型を作ることを考えます。普通はそんな型は必要ないし、当然その分オーバーヘッドが生じてしまうので利用する箇所は限られるだろうけどコンテナに格納するなどして便利なことはあるにはあります。
実装方法
Step1
Any型本体を考えましょう。
class Any { private: public: template <typename T> Any(T const& it) : data(new ###(it)) { } private: std::unique_ptr<###> data; };
とりあえずコンストラクタにデータを入れてそれをどんな方でも保存できなければ要件は満たせません。さて、###にはどんな型を入れればよいのでしょうか。ここはしかたがないのでテンプレートで頑張ることにします。即ち、なんらかのクラステンプレートが必要ということです。
class Any { private: template <typename T> class holder { public: holder(T const& it) : data(it) { } private: T data; }; public: template <typename T> Any(T const& it) : data(new holder<T>(it)) { } private: std::unique_ptr<holder<###>> data; };
Step2
さてこんどはデータの型が定まらない問題に直面しました。###はどうしたらいいのでしょうか? 複数のクラスを一つのクラスにまとめる…といえば抽象クラスが浮かびます。holder
class Any { private: class place_holder { public: virtual ~place_holder() { } }; template <typename T> class holder : public place_holder { public: holder(T const& it) : data(it) { } private: T data; }; public: template <typename T> Any(T const& it) : data(new holder<T>(it)) { } private: std::unique_ptr<place_holder> data; };
それでは実際に利用してみましょう。
Any any = 100; Any any2 = 10.5;
Step3
さて、格納した値を参照することを考えます。今現在の型が何かを知るには実行時に知るしかありません。したがって決め打ちで出力する方法が必要です。place_holderからholder
template <typename T> T& get() const { auto p = dynamic_cast<holder<T>*>(data.get()); if (p == nullptr) { throw std::bad_cast(); } return p->data; }
Step4
再代入やコピー等を考えましょう。他のAnyオブジェクトは当然コピーできますし、任意のオブジェクトはコピーされます。同様に代入もされます。結局次のように成りました。
typeとcloneが必要なのは、typeで型情報を得る時とAnyからAnyへコピーした時にひつようだからです。
class Any { private: struct place_holder { virtual ~place_holder() { } virtual std::type_info const& type() const { return typeid(nullptr); } virtual std::unique_ptr<place_holder> clone() const { return nullptr; } }; template <typename T> struct holder : public place_holder { holder(T const& it) : data(it) { } template <typename ...Args> holder(Args ...args) : data(args) { } std::type_info const& type() const override { return typeid(data); } T data; std::unique_ptr<place_holder> clone() const override { return std::make_unique<holder<T>>(data); } }; public: Any() {} template <typename T> Any(T const& it) : data(new holder<T>(it)) { } Any(Any const& it) : data(std::make_unique<place_holder>(*it.data)) { } Any(Any&& it) : data(std::move(it.data)) { } Any& operator=(Any const& it) { data = it.data->clone(); return *this; } Any& operator=(Any&& it) { data = std::move(it.data); return *this; } template <typename T> Any& operator=(T const& it) { data = std::make_unique<holder<T>(it); return *this; } template <typename T> T& get() const { auto p = dynamic_cast<holder<T>*>(data.get()); if (p == nullptr) { throw std::bad_cast(); } return p->data; } std::type_info const& type() const { return data->type(); } private: std::unique_ptr<place_holder> data; };
使う場合はこうします :
Any any = C(); // construct & copy printf("%s\n", any.type().name()); Any any2; any2 = any; // copy printf("%s\n", any2.type().name()); // destruct & destruct