プログラムがコンパイルされるまで、テンプレートは「汎用」です。その時点で、コンパイルは、処理する必要がある型を認識する必要があります。
コンパイル時に不明な(より良い:まだ知られていない)型テンプレートを含むことができるものが必要な場合、それは解決策ではありません。実際の型は実行時に認識されるため、最終的に「ハンドラー」内にラップされる、実行時ベースのポリモーフィズム (ポリモーフィック ベースからの継承) に移行する必要があります。
本質的には、型をチェックできる少なくとも仮想関数を持つベースと、その関数をall
型に適した方法で実装するジェネリック派生クラスが必要です。
boost::any は実装になる可能性がありますが、特に「ランタイム型を検出できる関数」が ... にすぎないことを考えると、より簡単な方法がある可能性がありますdynamic_cast
。
このような解決策にたどり着くことができます
#include <memory>
class any_value
{
template<class T>
class wrapper; //see below
class common_base
{
public:
virtual ~common_base() {} //this makes the type polymorphic
template<class T>
T* has_value()
{
auto* ptr = dynamic_cast<wrapper<T>*>(this);
return ptr? &ptr->m: nullptr;
}
};
template<class T>
class wrapper: public common_base
{
public:
wrapper() :m() {}
wrapper(const T& t) :m(t) {}
T m;
};
std::unique_ptr<common_base> pb;
public:
any_value() {}
template<class T>
any_value(const T& t) :pb(new wrapper<T>(t)) {}
template<class T>
any_value& operator=(const T& t)
{ pb = std::unique_ptr<common_base>(new wrapper<T>(t)); return *this; }
any_value(any_value&&) =default;
any_value& operator=(any_value&&) =default;
//NOW THE GETTERS
template<class T>
T* get() const //nullptr if not holding a T*
{ return bool(pb)? pb->has_value<T>(): nullptr; }
template<class T>
bool get(T& t)
{
T* pt = get<T>();
if(pt) t = *pt;
return bool(pt);
}
};
#include <iostream>
#include <string>
int main()
{
any_value a(5), b(2.7192818), c(std::string("as a string"));
int vi=0; double vd=0; std::string vs;
if(!a.get(vi)) vi=0; //will go
if(!a.get(vd)) vd=0; //will fail
if(!a.get(vs)) vs.clear(); //will fail
std::cout <<"vi = "<<vi<<", vd = "<<vd<<", vs = "<<vs<<" \n";
if(!b.get(vi)) vi=0; //will fail
if(!b.get(vd)) vd=0; //will go
if(!b.get(vs)) vs.clear(); //will fail
std::cout <<"vi = "<<vi<<", vd = "<<vd<<", vs = "<<vs<<" \n";
if(!c.get(vi)) vi=0; //will fail
if(!c.get(vd)) vd=0; //will fail
if(!c.get(vs)) vs.clear(); //will go
std::cout <<"vi = "<<vi<<", vd = "<<vd<<", vs = "<<vs<<" \n";
}
Abhinavのコメントに続いて:
C++ 型システムは静的であるため、「既知」である可能性があるものを最初に逆シリアル化しない限り、一般に「不明」を逆シリアル化することはできません。
このためには、まず C++ 型 (オブジェクトではない) を認識可能な値 (type-uid のようなもの) に再設定する方法と、それらの「値」に適したラッパーを作成する「ファクトリ」が必要です。
保存するときは、そのuid を保存してから、common_base 仮想関数を介してラップされた値を保存するように要求します。ロード時には、最初に uid をロードしてから、適切なタイプの新しいラッパーを作成し(後述)、common_base 仮想関数を介して値をロードします。
適切なラッパーを作成するには、タイプ uid に関連付けられたラッパーを作成する関数に uid-s をマップするテーブルが必要です。このテーブルは、シリアル化/逆シリアル化できるようにするために必要なすべての型に対して事前に初期化する必要があります。
しかし、これはシリアライゼーション/デシリアライゼーションについて話していない元の質問から遠く離れています。
問題が「シリアル化」である場合、「型消去」は完全な解決策ではありません。「ファクトリーパターン」をもっとよく見る必要があります。そして、その議論により適した別の質問を投稿してください。