在编译程序之前,模板是“通用的”。此时必须让编译器知道它必须处理什么类型。
如果您想要一些可以包含编译时未知(更好:未知)类型模板的东西不是解决方案。由于实际类型仅在运行时才知道,因此您必须转向基于运行时的多态(从多态基继承),最终包装在“处理程序”中。
本质上,您需要一个至少具有允许您检查类型的虚函数的基类,以及以适合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++ 类型(不是对象)重新预置为可识别值(类型 uid)的方法,以及创建适合这些“值”的包装器的“工厂”。
保存时,您只需保存该uid,然后通过 common_base 虚函数请求保存包装的值。加载时,首先加载 uid,然后创建一个具有适当类型的新包装器(见下文),然后通过 common_base 虚函数加载值。
要创建适当的包装器,您需要一个将 uid-s 映射到创建与类型 uid 关联的包装器的函数的表。该表必须针对您需要能够序列化/反序列化的所有类型进行预初始化。
但这远离了你原来的问题,这并没有谈到序列化/反序列化。
如果问题是“序列化”,那么“类型擦除”并不是一个完整的解决方案。您应该更多地关注“工厂模式”。并发布另一个更适合该论点的问题。