不同类别的向量

时间:2018-09-12 17:33:40

标签: c++ templates c++17

我有一个带有向量的类,我想用用户选择的两种类型之一来填充。让我们将我的类称为option1和option2

我想做的事情

class storage_class 
{
public:
    storage_class(int sel, int n)
    {
        if(sel == 1)
           for(int i = 0; i < n; i++) 
               my_store.push_back(std::make_unique<option1>());    
        else if(sel == 2)
           for(int i = 0; i < n; i++)
               my_store.push_back(std::make_unique<option2>());
    }

private:
    // Something like this but that actually works
    std::vector<T> my_store;
};

然后,我想像这样或类似方式使用它,因此无需根据所选选项修改此用法。

int main()
{
    storage_class store(1);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

类option1和option2将是数学模拟,它将创建数据并将其自身存储在作为该类成员的向量中。

我想将一个选项的多个实例存储在向量中,然后从那里进行操作。我可以使用C ++ 17。

3 个答案:

答案 0 :(得分:3)

使用c ++ 17时,您可以简单地将std::variant用作容器的类型,该容器本身可以保留所有想要的类型。

示例:

class A { public: void Do() { std::cout << "A::Do" << std::endl; } };
class B { public: void Go() { std::cout << "B::Go" << std::endl; } };


template<class... Ts> struct funcs : Ts... { using Ts::operator()...; };
template<class... Ts> funcs(Ts...) -> funcs<Ts...>;

int main()
{
    std::vector<std::variant<A,B>> vec;
    vec.push_back(A{});
    vec.push_back(B{});

    for ( auto& el: vec)
    {
        std::visit( funcs{ [](A& a){ a.Do(); }, [](B& b) { b.Go(); } }, el);
    }
}

输出:

A::Do
B::Go

类是完全独立的,可以使用std::visit简单地调用方法,并在此处传递可调用对象。我提供了一个简单的funcs实现,它简单地收集了所有可调用实体,从而简化了将调用与不同无关类的不同方法接口的操作。

由于std::variant是一种带标记的联合,因此需要存储您所使用的最大类型的联合存储。如果这浪费了很多内存,则可以存储指向该实例的指针,如果需要内存管理方面的帮助,可以使用std::unique_ptrstd::shared_ptr来存储该实例;)

答案 1 :(得分:2)

标准方法是从option1制作option2base_class派生的类,这似乎与示例main()一致。使用通用的Factory类模板,下面是一个示例:

#include <functional>
#include <iostream>
#include <memory>
#include <unordered_map>
#include <vector>

// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
    using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
    const Map mMap;
  public:
    Factory(Map&& map):mMap(std::move(map)) { }
    std::unique_ptr<T> operator()(const K& key, Ts... args) const
    {
        const typename Map::const_iterator itr = mMap.find(key);
        return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
    }
};

class base_class
{
  public:
    virtual void create_data() = 0;
};

class option1 : public base_class
{
  public:
    void create_data() override
    {
        std::cout << "I'm option1." << std::endl;
    }
};

class option2 : public base_class
{
  public:
    void create_data() override
    {
        std::cout << "I'm option2." << std::endl;
    }
};

class storage_class 
{
    using SimulationFactory = Factory<int,base_class>; // Optionally add constructor parameter types
    const SimulationFactory simulation_factory; // This can be made static const.
public:
    storage_class(int sel, int n)
    :   simulation_factory(
            { { 1, []() { return std::make_unique<option1>(); } }
            , { 2, []() { return std::make_unique<option2>(); } }
            })
    {
        for (int i = 0; i < n; i++) 
            my_store.push_back(simulation_factory(sel));
    }

    std::vector<std::unique_ptr<base_class>> my_store;
};

int main()
{
    int n_iterations = 4;

    storage_class store(1, n_iterations);

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

这是使用g++ -std=c++17 main.cc在Linux上为我编译的。

此代码可以进行一些改进,但是我复制了您的main()函数以说明基本概念。希望有帮助。


2018年9月21日-如何将参数传递给构造函数的示例。

文件:factory.h

#pragma once

#include <functional>
#include <memory>
#include <unordered_map>

// Generic Factory class template
template<typename K,typename T,typename... Ts>
class Factory
{
    using Map = std::unordered_map<K, std::function<std::unique_ptr<T>(Ts...)>>;
    const Map mMap;
  public:
    Factory(Map&& map):mMap(std::move(map)) { }
    std::unique_ptr<T> operator()(const K& key, Ts... args) const
    {
        const typename Map::const_iterator itr = mMap.find(key);
        return itr == mMap.cend() ? nullptr : itr->second(std::forward<Ts>(args)...);
    }
};

文件:main.cc

#include "factory.h"

#include <iostream>
#include <string>
#include <vector>

class base_class
{
  public:
    virtual void create_data() = 0;
};

class option1 : public base_class
{
    const double mD;
  public:
    option1(double d)
    :   mD(d)
    { }
    void create_data() override
    {
        std::cout << "I'm option1: mD("<<mD<<')' << std::endl;
    }
};

class option2 : public base_class
{
    const double mD;
  public:
    option2(double d)
    :   mD(d)
    { }
    void create_data() override
    {
        std::cout << "I'm option2: mD("<<mD<<')' << std::endl;
    }
};

class storage_class 
{
    using SimulationFactory = Factory<int,base_class,double>; // Optionally add constructor parameter types
    const SimulationFactory simulation_factory; // This can be made static const.
public:
    storage_class(int sel, int n)
    :   simulation_factory(
            { { 1, [](double d) { return std::make_unique<option1>(d); } }
            , { 2, [](double d) { return std::make_unique<option2>(d); } }
            })
    {
        for (int i = 0; i < n; i++) 
            my_store.push_back(simulation_factory(sel,static_cast<double>(i)));
    }

    std::vector<std::unique_ptr<base_class>> my_store;
};

int main()
{
    int n_iterations = 4;

    storage_class store1(1, n_iterations);
    storage_class store2(2, n_iterations);

    for(int i = 0; i < n_iterations; i++)
    {
        store1.my_store[i]->create_data();
        store2.my_store[i]->create_data();
    }
}

输出:

I'm option1: mD(0)
I'm option2: mD(0)
I'm option1: mD(1)
I'm option2: mD(1)
I'm option1: mD(2)
I'm option2: mD(2)
I'm option1: mD(3)
I'm option2: mD(3)

答案 2 :(得分:2)

这里是一个示例,尝试使用类storage_class上的模板参数,使其尽可能与您的示例保持紧密联系。请参见工作版本here。我仅添加了option1,并在您通过my_store函数访问成员main时将其公开。

#include <memory>
#include <vector>
#include <iostream>

struct option1{
    void create_data(){ std::cout << "created\n"; }
};


template<typename T>
class storage_class 
{
public:
    storage_class(int n)
    {
       for(int i = 0; i < n; i++) 
           my_store.push_back(std::make_unique<T>());    
    }

    std::vector<std::unique_ptr<T>> my_store;
};

int main()
{
    storage_class<option1> store(4);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        store.my_store[i]->create_data();
    }
}

另一种选择是使用std::variant。请参见工作版本here

#include <memory>
#include <vector>
#include <variant>
#include <iostream>

struct option1{
    void create_data(){ std::cout << "created 1\n"; }
};

struct option2{
    void create_data(){ std::cout << "created 2\n"; }
};


class storage_class 
{
public:

    using option = std::variant<std::unique_ptr<option1>,std::unique_ptr<option2>>;

    storage_class(int sel, int n)
    {
        if(sel == 0)
           for(int i = 0; i < n; i++) 
               my_store.push_back(option(std::make_unique<option1>()));    
        else if(sel == 1)
           for(int i = 0; i < n; i++)
               my_store.push_back(option(std::make_unique<option2>()));
    }


    std::vector<option> my_store;
};

int main()
{
    storage_class store(1, 4);

    int n_iterations = 4;

    for(int i = 0; i < n_iterations; i++)
    {
        std::get<1>(store.my_store[i])->create_data();
    }
}