我正在尝试将模板类设置为占位符类,它可以包含类似于字符串和类型的T对象。下面是我为此编写的代码。
#include <iostream>
#include <string>
#include <map>
using namespace std;
//A class which act as placeholder to hold
//unknown object. Something similar as Object
//in Java
template <typename T>
class Genric
{
public:
map<string, T> addP; //This will be placeholder for time
// being.
};
class A
{
public:
Genric t1; //Have object of Genric class so that we can
// access the member variable in future.
void foo()
{
cout<<"Calling foo"<<endl;
}
};
int main()
{
A a1;
a1.foo();
}
但是当我尝试编译时,我遇到了错误。
$ g++ tempClass.cxx
tempClass.cxx:21:9: error: invalid use of template-name 'Genric' without an argument list
上面Genric类的目的只是作为一个可以在将来填充的成员变量的占位符类。 那么有没有办法可以编写这样的Genric类。
答案 0 :(得分:2)
您需要boost::any
之类的内容:
map<string, boost::any> anywayzz;
您可以在其中存储任何对象。您不需要Genric
课程模板。
如果您没有使用提升,那么您可以自己实施any
。在此站点上查找其实现或 type-erasure 。你肯定会有所了解。从这里开始:
答案 1 :(得分:2)
您将Genric
定义为模板类,但之后尝试初始化t1
而不给它类型。这就是你得到的错误。试试例如:
Genric<int> t1;
或者,如果您正在寻找真正的运行时通用,请查看 boost :: any 。
答案 2 :(得分:2)
在编译程序之前,模板是“通用的”。此时,必须使编译知道它必须处理的类型。
如果你想要一些可以包含编译时未知的东西(更好:尚未知道),那么类型模板就不是解决方案。由于实际类型将在运行时才知道,因此您必须转向基于运行时的多态(从多态基础继承),最终包含在“处理程序”中。
从本质上讲,你需要一个基于虚拟函数的基础,它允许你检查类型,以及以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类型关联的包装器的函数。 必须为您需要能够序列化/反序列化的所有类型预先初始化此表。
但这远远超出了您的原始问题,而不是关于序列化/反序列化。
如果问题是“序列化”,则“类型擦除”不是一个完整的解决方案。你应该更多地看看“工厂模式”。并发布另一个更适合该论点的问题。