1

我正在尝试制作一个模板类作为占位符类,它可以包含类似字符串和类型 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 类。

4

3 回答 3

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 关联的包装器的函数的表。该表必须针对您需要能够序列化/反序列化的所有类型进行预初始化。

但这远离了你原来的问题,这并没有谈到序列化/反序列化。

如果问题是“序列化”,那么“类型擦除”并不是一个完整的解决方案。您应该更多地关注“工厂模式”。并发布另一个更适合该论点的问题。

于 2013-03-08T08:43:37.687 回答
2

你需要类似的东西boost::any

map<string, boost::any> anywayzz;

您可以在其中存储任何对象。你不需要Genric类模板。

如果你不使用 boost,那么你可以any自己实现。在此站点上查找其实现或类型擦除。你肯定会有一些想法。从这里开始:

于 2013-03-08T07:32:48.537 回答
2

您正在定义Genric为模板类,但随后尝试在t1不给它类型的情况下进行初始化。那就是你得到的错误。尝试例如:

Genric<int> t1;

或者,如果您正在寻找真正的运行时泛型,请查看boost::any

于 2013-03-08T07:32:56.140 回答