将不同的模板化类存储在一个容器中,而不会丢失有关其类型

时间:2017-07-06 11:01:01

标签: c++ templates c++14

我目前正在开发一个项目,其中我的应用程序的客户端部分必须能够在服务器上创建自定义模板化类。服务器部分必须跟踪这些创建的类,并且必须记住已经实例化类的类型。问题是,在我的应用程序中有大约36种不同的类模板组合是有效的。我目前正在努力跟踪集合中的这些不同类型,而不会丢失有关我的实例的信息。

我目前正在使用这样的东西:

#include <memory>
#include <type_traits>
#include <vector>

enum class data_type : std::uint8_t {
    type_int = 1,
    type_float,
    type_double
};

enum class class_type : std:: uint8_t {
    type_A = 1,
    type_B
};

struct X {
    virtual data_type get_data_type() = 0;
    virtual class_type get_class_type() = 0;
};

template <typename T>
struct A : X {
    data_type get_data_type() override
    {
        if (std::is_same<T, int>::value) {
            return data_type::type_int;
        } else if (std::is_same<T, float>::value) {
            return data_type::type_float;
        } else if (std::is_same<T, double>::value) {
            return data_type::type_double;
        } else {
            /* ... */
        }
    }

    class_type get_class_type() override
    {
        return class_type::type_A;
    }
};

template <typename T>
struct B : X {
    data_type get_data_type() override
    {
        if (std::is_same<T, int>::value) {
            return data_type::type_int;
        } else if (std::is_same<T, float>::value) {
            return data_type::type_float;
        } else if (std::is_same<T, double>::value) {
            return data_type::type_double;
        } else {
            /* ... */
        }
    }

    class_type get_class_type() override
    {
        return class_type::type_B;
    }
};

struct Storage {

    template <typename T, template <typename> class Class>
    void create() {
        Class<T>* t = new Class<T>();
        _classes.push_back(std::unique_ptr<X>(t));
    }

    std::vector<std::unique_ptr<X>> _classes;
};

但我想知道这是否可行,或者是否有更优雅的方式。在这里,我必须始终切换enum以获取我的Storage类的完整类型,例如:

switch(_classes.front()->get_class_type()) {
case class_type::type_A:
{
    switch(_classes.front()->get_data_type()) {
    case data_type::type_int:
    {
         /* I finally know that it is A<int> */
    }
/* ... */

提前致谢。

2 个答案:

答案 0 :(得分:3)

您可以考虑使用std::variantstd::visit模式

auto var = std::variant<int, float, double>{};
// assign var to value
std::visit([](auto& value) {
    using Type = std::decay_t<decltype(value)>;
    if constexpr (std::is_same<Type, int>{}) {
        // is an int
    } else if (std::is_same<Type, float>{}) {
        // is float
    } else if (std::is_same<Type, double>{}) {
        // is double
    }
}, var);

如果if constexpr看起来很难看,那么您也可以用手动的访客类替换它。

class Visitor {
public:
    void operator()(int& value) { ... }
    void operator()(float& value) { ... }
    void operator()(double& value) { ... }
};

auto var = std::variant<int, float, double>{};
// assign var to value
std::visit(Visitor{}, var);

答案 1 :(得分:2)

正如对问题的评论所述,这是一种可行的方法,可以提供帮助:

#include<vector>
#include<memory>

struct Counter {
    static int next() {
        static int v = 0;
        return v++;
    }
};

template<typename>
struct Type: Counter {
    static int value() {
        static const int v = Counter::next();
        return v;
    }
};

struct X {
    virtual int get_data_type() = 0;
    virtual int get_class_type() = 0;
};

template <typename T>
struct A : X {
    int get_data_type() override {
        return Type<T>::value();
    }

    int get_class_type() override {
        return Type<A<T>>::value();
    }
};

template <typename T>
struct B : X {
    int get_data_type() override {
        return Type<T>::value();
    }

    int get_class_type() override {
        return Type<B<T>>::value();
    }
};

struct Storage {
    template <typename T, template <typename> class Class>
    void create() {
        Class<T>* t = new Class<T>();
        _classes.push_back(std::unique_ptr<X>(t));
    }

    std::vector<std::unique_ptr<X>> _classes;
};

int main() {
    Storage s;
    s.create<int, A>();

    if(Type<int>::value() == s._classes.front()->get_class_type()) {
        //...
    };
}

wandbox上看到它正在运行。