C ++ возможно ли иметь контейнер специализированных шаблонов объектов с параметрами разных типов? - PullRequest
2 голосов
/ 06 января 2012

У меня есть шаблонный параметр класса, который может (или должен) быть специализированным.Я хочу поместить все свои параметры в контейнер.Как это сделать, если мои параметры создаются с разными типами?

В классе Container I хотел бы иметь векториз разных типов (int, double, ...) или что-то эквивалентное, что кажется невозможным.

Если класс Parameter является производным от базового класса, то контейнер может объявить вектор как вектор,Но в этом случае мы ничего не можем сделать с помощью 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 ]

2 голосов
/ 06 января 2012

Правильное решение - написать достаточно хороший интерфейс для базового класса, чтобы вы могли делать все, что вам нужно:

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;
};

Хотя это может быть не то, что вам нужно, он все же позволяет передавать значенияот одного параметра к другому.Как только вы хотите получить фактическое значение, вам нужно знать тип во время компиляции.Случай переключения для типа может помочь сделать его во время выполнения.

2 голосов
/ 06 января 2012

Вы можете использовать Boost.Any , который может содержать данные любого типа. Затем вы будете использовать boost::any_cast<> для преобразования объекта обратно в правильный тип.

Кроме этого, вам придется использовать подход базового класса, но, как вы упомянули, может быть трудно заставить Container::foo сделать что-нибудь полезное.

Один из способов решения этой проблемы - заставить все ваши foo функции принимать строку в качестве параметра, тогда каждая конкретная реализация функции будет анализировать эту строку и преобразовывать ее в правильный тип.

Редактировать: Повышение. Любой пример:

#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";
}
0 голосов
/ 06 января 2012

Каждая вещь внутри контейнера должна быть одного типа.Я сделал нечто похожее на ваш подход, где я создал базовый класс, который имел некоторый полезный универсальный интерфейс и производный класс был создан по шаблону.Единственный другой способ приблизиться к решению будет включать определение функции базового класса, возвращающей значение для указания типа, и затем уменьшение базы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...