C ++是否可以拥有一个具有不同类型参数的专用模板对象的容器?

时间:2012-01-05 20:33:22

标签: c++ templates containers

我有一个模板化的类参数,可以(或必须)专门化。 我想把所有参数放在一个容器中。 如果我的参数是用不同类型实例化的,怎么做?

在类Container中,我 喜欢有一个向量< Parameter *>来自不同类型(int,double,...)或类似的东西似乎是不可能的。

如果Parameter类是从基类派生的,那么Container可以将vect声明为vector< Base *>。但在这种情况下,我们在Container :: foo中无能为力。

以下是我的源代码示例。我的一个参数是QString,它与ostream不兼容。

感谢您的评论。



    #include <QString>
    #include <vector>
    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;

    #define P(a) cout << #a << ":" << a << endl

    /*
    class Base {

    };
    */

    template<typename T> class Parameter /*: public Base */ {
    private:
        T val;
    public:
        void setVal(const T &val) {
            this->val = val;
        }
        const T &getVal() {
            return val;
        }
        string getFoo() {
            stringstream s;
            s << val;
            return s.str();
        }
    };

    template<>
    string Parameter<QString>::getFoo() {
        stringstream s;
        s << val.toStdString();
        return s.str();
    }

    class Container {
    public:
        void push_back(Parameter *base) {
            vect.push_back(base);
        }
        void foo() {
            /* do something with the parameters */
        }
    private:
        vector<Parameter*> vect;
    };

    int main() {
        Parameter<int> pi;
        Parameter<QString> ps;

        pi.setVal(10);
        ps.setVal("QString");

        P(pi.getVal());
        P(ps.getVal().toStdString());

        P(pi.getFoo());
        P(ps.getFoo());

        Container container;
        container.push_back(&pi);
        container.push_back(&ps);
    }

非常感谢你的评论。我会按照你的建议使用boost :: any。 这是更新版本:



    #include <boost/any.hpp>
    #include <QString>
    #include <vector>
    #include <iostream>
    #include <string>
    #include <sstream>
    using namespace std;

    #define P(a) cout << #a << ":" << a << endl

    template<typename T> class Parameter {
    private:
        T val;
    public:
        void setVal(const T &val) {
            this->val = val;
        }
        const T &getVal() {
            return val;
        }
        string getFoo() {
            stringstream s;
            s << val;
            return s.str();
        }
    };

    template<>
    string Parameter<QString>::getFoo() {
        stringstream s;
        s << val.toStdString();
        return s.str();
    }

    class Container {
    public:
        void push_back(boost::any base) {
            vect.push_back(base);
        }
        void foo() {
            cout << "do something with the parameters\n";
            for (vector<boost::any>::iterator i = vect.begin(); i != vect.end(); ++i) {
                boost::any a = (*i);
                if (a.type() == typeid(Parameter<int>*)) {
                    Parameter<int> *ai = boost::any_cast<Parameter<int> *>(a);
                    cout << ai->getFoo() << endl;
                } else if (a.type() == typeid(Parameter<QString>*)) {
                    Parameter<QString> *aq = boost::any_cast<Parameter<QString> *>(a);
                    cout << aq->getFoo() << endl;
                } else {
                    cout << "unknown type:" << a.type().name() << endl;
                }
            }
        }
    private:
        vector<boost::any> vect;
    };

    int main() {
        Parameter<int> pi;
        Parameter<QString> ps;

        pi.setVal(10);
        ps.setVal("QString");

        P(pi.getVal());
        P(ps.getVal().toStdString());

        P(pi.getFoo());
        P(ps.getFoo());

        Container container;
        container.push_back(&pi);
        container.push_back(&ps);
        container.foo();
    }



3 个答案:

答案 0 :(得分:2)

您可以使用Boost.Any来保存任何类型的数据。然后,您可以使用boost::any_cast<>将对象转换回正确的类型。

除此之外,你将不得不采用基类方法,但正如你所提到的,可能很难让Container::foo做任何有用的事情。

解决此问题的一种方法是让所有foo函数都将字符串作为参数,然后函数的每个特定实现都将解析该字符串并将其转换为正确的类型。

编辑: Boost.Any示例:

#include <iostream>
#include <boost/any.hpp>

int main()
{
    boost::any param = 89;

    // This will fail because `param` is currently holding an int
    // not a char
    char ch = boost::any_cast<char>(param);

    // This works
    int i = boost::any_cast<int>(param);

    // You can always change the value and type of what
    // `param` is holding
    param = "example";
}

答案 1 :(得分:2)

正确的解决方案是为Base类编写足够好的接口,以便您可以执行所需的所有操作:

class Base {
public:
  virtual void *GetVal() const=0;
  virtual void SetVal(void *ptr)=0;
  virtual std::string Type() const=0;
  virtual std::string GetAsString() const=0;
};

虽然这可能不是您想要的,但仍允许将值从一个参数传递到下一个参数。一旦想要实际值,就需要知道编译时的类型。该类型的Switch-case可能有助于使其成为运行时。

答案 2 :(得分:0)

容器内的每件东西都必须是同一类型。我已经做了类似于你的方法,我创建了一个基类,它有一些有用的泛型接口,派生类是模板化的。接近解决方案的唯一方法是定义基类函数以返回值以指示类型,然后向下转换基础。