【问题标题】:C++ is it possible to have a container of specialized templates objects with different types parameters?C++ 是否可以拥有一个包含不同类型参数的专用模板对象的容器?
【发布时间】:2012-02-03 16:24:06
【问题描述】:

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

在 Container 类中,我希望拥有一个来自不同类型(int、double、...)的向量 或类似的东西,这似乎是不可能的。

如果 Parameter 类派生自基类,则 Container 可以将 vect 声明为 vector。但是在这种情况下,我们不能在 Container::foo 中做任何特定的事情。

以下是我的源示例。我的参数之一是与 ostream 不兼容的 QString。

感谢您的 cmets。



    #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();
    }



【问题讨论】:

  • Qstring“与ostream不兼容”是什么意思,它与这个问题有什么关系?您是否尝试调用 Parameter::getFoo()?如果有意义,您可以提供一种方法让 operator
  • 您到底想做什么?使用 Base 如何不能解决您的问题?听起来您想要跨多种类型通用的东西,同时具有特定于该类型的一些功能(“我们不能在 Container::foo 中做任何特定的事情”) - 我不确定如何在不向下转换的情况下有意义地实现这一点。
  • @bacar 模板类中的 getFoo() 不适用于 QString。我们必须使用:s << val.toStdString().
  • 如果你为 QString 提供一个操作符. 提供模板特化
  • 是的,您适合 QString 的运算符。这只是专业化的一个例子。 Container 类的问题仍然存在:如何在容器中存储任何类型的实例化参数?有可能吗?

标签: c++ templates containers


【解决方案1】:

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

除此之外,您必须采用基类方法,但正如您所提到的,可能很难让 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";
}

【讨论】:

  • 您的最后一条评论还可以,但问题仍然存在:如何存储任何类型的实例化参数?您有使用 Boost.Any 的示例吗?
  • @user1132852 我已经发布了一个关于如何使用 Boost.Any 的极简示例。
  • Boost.Any 看起来有点矫枉过正; OP 希望包含相关类型,而不是 any 类型。 vector<:any> 似乎不允许您将容器限制为 Parameter 对象。您从 Boost.Any 中获得了哪些您无法从简单的 dynamic_cast 中获得的优势?
  • @bacar 这只是一种选择。我还建议使用带有虚拟方法foo 的基类。这取决于 OP 他们更喜欢哪种替代方案。
【解决方案2】:

容器内的所有东西都必须是同一类型。我做了一些类似于你的方法的事情,我创建了一个具有一些有用的通用接口的基类,并且派生类是模板化的。解决方案的唯一其他方法是定义一个基类函数以返回一个值来指示类型,然后向下转换基类。

【讨论】:

    【解决方案3】:

    正确的解决方案是为 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 可能有助于使其运行时。

    【讨论】:

    • 像这样使用void* 很容易出错。我肯定会把它改成boost::any
    • 是的,但是为了在我的 Container 类中切换大小写,我需要了解所有案例>
    • 它不会比使用 any_cast 更容易出错。垂头丧气的操作总是很危险的,虚空*只是记录这里正在发生危险的事情。
    • @tp1 错了。使用 any_cast 如果你使用了错误的类型,你会得到一个异常。使用void*,您无法确定是否将其转换回正确的类型。
    • @tp1 快速失败仍然比只在路上可见的无声错误好很多倍。
    猜你喜欢
    • 2013-06-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-10-31
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    相关资源
    最近更新 更多