уникальный контейнер для шаблона класса - PullRequest
0 голосов
/ 05 сентября 2018

У меня есть алгоритм (здесь не прописан), который принимает в качестве входных данных различные параметры (int, float, vector). Моя идея дизайна заключалась в том, чтобы иметь контейнер, который содержит все эти разные параметры.
Для этого у меня есть базовый класс Parameter и производный шаблон класса TypeParameter. Эти параметры будут храниться в контейнере.

Дизайн представлен ниже:

    #pragma once

#include <utility>
#include <memory>
#include <string>
#include <vector>

namespace parameter
{
    /*
        Interface for parameter
    */
    class Parameter
    {
    public:
        Parameter() {}
        Parameter(std::string param_name) : name(param_name) {}
        Parameter(const Parameter&& other) noexcept : name(std::move(other.name)) {}
        virtual ~Parameter() {}

        inline const std::string get_name() { return name;  }

    private:
        std::string name;
    };

    /*

    */
    template<class T>
    class TypeParameter
        : public Parameter
    {
    public:
        TypeParameter(std::string param_name, T new_value) : Parameter(param_name), value(new_value) {}
        TypeParameter(const TypeParameter&& other) noexcept : Parameter(std::move(other)), value(std::move(other.T)) {}

        inline const T get_value() { return value; }

    private:
        T value;
    };

    /*
        Container for parameters
    */

    class ParameterSet
    {
    public:
        ParameterSet() {}

        void add(std::unique_ptr<Parameter> param) { data.push_back(std::move(param)); }

    private:
        std::vector <std::unique_ptr<Parameter>> data;
    };

} //namespace parameter

Основное:

    #include <iostream>
#include <string>

#include "Parameter.h"

using parameter::TypeParameter;
using parameter::Parameter;
using parameter::ParameterSet;

void foo(std::unique_ptr<Parameter> p)
{
    std::cout << p->get_value(); // ERROR
}

int main(int argc, char *argv[])
{
    TypeParameter<int> *iparam = new TypeParameter<int>("ee", 3);
    std::unique_ptr<Parameter> p = std::make_unique <TypeParameter<int>>("foo", 3);


    foo(std::move(p));

    ParameterSet param_set;
    param_set.add(std::unique_ptr<Parameter>(iparam));
    param_set.add(std::move(p));

    getchar();
}

Моя проблема в том, что я не могу получить значение без приведения.

Следовательно, мой вопрос заключается в том, как преобразовать unique_ptr из класса Parameter в производный TypeParameter. Есть ли другой способ дизайна контейнера?

Большое спасибо!

Ответы [ 2 ]

0 голосов
/ 06 сентября 2018

Альтернативой std::variant / std::any является старый способ полиморфизма:

class Parameter
{
public:
    Parameter(const std::string& param_name) : name(param_name) {}
    virtual ~Parameter() = default;

    const std::string& get_name() const { return name;  }

    virtual void printValue() const = 0;
    // Other virtual methods

private:
    std::string name;
};

template<class T>
class TypeParameter : public Parameter
{
public:
    TypeParameter(const std::string& name, const T& t) : Parameter(name), value(t) {}

    // Non virtual method when we don't access it by base class.
    const T& get_value() const { return value; }

    void printValue() const { std::cout << value; }

private:
    T value;
};

А потом твой

void foo(const Parameter& p)
{
    std::cout << p.get_value(); // ERROR
}

становится

void foo(const Parameter& p)
{
    p.print();
}

Если вы не хотите добавлять много виртуальных методов в Parameter, тогда Шаблон посетителя может помочь, но тогда вы должны знать каждый производный тип.

0 голосов
/ 05 сентября 2018

Вам не нужно изобретать велосипед. Есть пара классов, которые вы можете использовать из стандартной библиотеки:

std::variant.

Как следует из комментариев, variant - это безопасное от типа объединение предопределенного набора типов данных, которое вы вводите в аргумент шаблонов variant. Например, std::variant<int,float,double> может содержать любое значение типа int, float или double, но ничего больше.

Чтобы использовать сохраненное значение, вы можете использовать шаблон посетителя с функцией std::visit(). Другие функции позволяют вам узнать, какой из предустановленных типов хранится в переменной (index()), и извлечь из нее значение (используя get()). Если вы попытаетесь извлечь значение неправильного типа, функция get() выдает исключение

std::any

- еще одна утилита, которая может содержать разные типы данных. В отличие от variant, вам не нужно знать типы во время компиляции. По сути, он сохраняет void* данных с typeinfo для запоминания своего первоначального типа. Затем вы можете использовать any_cast, чтобы привести переменную к исходному типу. Как и variant, при попытке привести к неверному типу выдается исключение.

Эти два класса доступны в C ++ 17. Если эти функции вам недоступны, они также были включены в надстройку (соответственно boost:variant и boost:any)

Вы можете хранить набор значений в стандартном контейнере библиотеки, например, в std::vector<std::variant<int,float,double>> или std::vector<std::any>>.

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