如何创建异构对象的集合

时间:2018-02-24 06:25:51

标签: c++

我想创建一个异构对象的集合;即。不同类型的物体。

当这些对象具有相似的功能(包括成员)但不从相同的父类派生时,这将非常有用。

一个完美的例子是随机数引擎: minstd_rand mt19937 ranlux24 都是引擎。它们具有相同的成员(例如调用operator()),但不是从常见的“Engine”类派生的,因此具有不同的类型。

随机数分布也是如此。

如果有一个共同的根类'Engine',我可以很容易地创建这些对象的向量,如下所示:

vector<Engine> engines {minstd_rand, mt19937, ranlux24};

完成此操作后,我可以在循环中调用函数,如下所示:

/// Generate 10 random numbers.
void gen(vector<Engine>& engines)
{
    for (auto& e : engines)
       for (int i = 0; i < 10; i++)
           cout << e() << endl;
}


int main()
{
    gen(engines);       /// Invocation
}

但是,我不能这样做。

如果我使用元组来包装每个引擎,则每个对象将具有不同的类型:

tuple<type1>, tuple<type2>, .... 

同样,类型将是异构的,我无法创建它们的集合。

所以问题是,是否有可能创建异构对象的集合,如果是,那么如何?

3 个答案:

答案 0 :(得分:2)

您可以使用vector<function<size_t ()>>来保留这些引擎。

using Engine = function<size_t ()>;
vector<Engine> engines = {minstd_rand{}, mt19937{}, ranlux24{}};
for (auto &e : engines) {
    cout << e() << endl;
}

答案 1 :(得分:1)

您可以简单地创建自己的多态层次结构来包装不同的单独类型的伪随机数生成器。由于不同的标准生成器具有公共接口,即使它们执行此操作,这也变得更加容易不是来自共同的基本类型。

有点像这样:

// Base interface class
class prng
{
public:
    using dist_type = std::uniform_int_distribution<int>;

    virtual ~prng() = default;

    virtual int operator()(int min, int max) = 0;

protected:
    dist_type dist;

    template<typename PRNG>
    static PRNG& eng()
    {
        thread_local static PRNG eng{std::random_device{}()};
        return eng;
    }
};

// smart pointers because polymorphism
using prng_uptr = std::unique_ptr<prng>;

// Generic class takes advantage of the different PRNG's
// similar interfaces
template<typename PRNG>
class typed_prng
: public prng
{
public:
    int operator()(int min, int max) override
        { return dist(eng<PRNG>(), dist_type::param_type(min, max)); }
};

// Some nice names
using prng_minstd_rand = typed_prng<std::minstd_rand>;
using prng_mt19937 = typed_prng<std::mt19937>;
using prng_ranlux24 = typed_prng<std::ranlux24>;

int main()
{
    // A vector of smart base pointers to typed instances
    std::vector<prng_uptr> prngs;

    // Add whatever generators you want
    prngs.push_back(std::make_unique<prng_minstd_rand>());
    prngs.push_back(std::make_unique<prng_mt19937>());
    prngs.push_back(std::make_unique<prng_ranlux24>());

    // numbers between 10 and 50    
    for(auto const& prng: prngs)
        std::cout << (*prng)(10, 50) << '\n';
}

答案 2 :(得分:-1)

[ver 1] @ Galik的帖子有效地展示了创建多态层次结构的基本技术,我将在下面解释。

该帖子旨在作为所涉及技术的演示(而非实施)。因此,它不编译: http://coliru.stacked-crooked.com/a/0465c2a11d3a0558

我已经纠正了根本问题,以下版本有效[ver 2]:
http://coliru.stacked-crooked.com/a/9bb0f47251e6dfed

所涉及的技术很重要。我已经投了@ Galik的帖子。

然而,@ Galik的解决方案存在1个问题:random_device()引擎在基类本身中进行了硬编码。因此,无论在子类中将哪个引擎作为参数传递,它都始终使用。事实上,作为参数传递给子类的引擎应该被用作随机数的来源。

我已更正此内容并更改了以下版本[ver 3]中的一些名称: http://coliru.stacked-crooked.com/a/350eadb55a4bafe7

#include <vector>
#include <memory>               /// unique_ptr
#include <random>
#include <iostream>

/// Declarations ...
class RndBase;          /// Random number base class

/// Generic Random number sub-class
/// takes advantage of the different Engines' similar interfaces
template<typename Eng>
class RndSub;

template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args);


/// Implementation ...

/// Random number base class
class RndBase
{
public:
    using dist_type = std::uniform_int_distribution<int>;

    virtual ~RndBase() = default;

    virtual int operator() (int min, int max) = 0;

protected:
    dist_type dist;

    /// factory method
    template<typename Eng>
    static Eng& eng()
    {
        static Eng eng {Eng {}};

        return eng;
    }
};   // RndBase


/// Generic Random number sub-class
/// takes advantage of the different Engines' similar interfaces
template<typename Eng>
class RndSub : public RndBase
{
public:
    /// Generate a random number.
    int operator() (int min, int max) override
    {
        return dist(eng<Eng>(), dist_type::param_type(min, max));
    }
};


int main()
{
    using Eminstd_rand = RndSub<std::minstd_rand>;
    using Emt19937 = RndSub<std::mt19937>;
    using Eranlux24 = RndSub<std::ranlux24>;

    /// smart pointers because of polymorphism
    using pRndBase = std::unique_ptr<RndBase>;

    /// A vector of smart base pointers to typed sub-classes
    std::vector<pRndBase> prndbases;

    /// Add whatever generators you want
    prndbases.push_back(make_unique<Eminstd_rand> ());
    prndbases.push_back(make_unique<Emt19937> ());
    prndbases.push_back(make_unique<Eranlux24> ());

    /// random numbers between 10 and 50
    for(auto const& prb : prndbases)
        std::cout << (*prb) (10, 50) << std::endl;
}


template<typename T, typename... Args>
std::unique_ptr<T> make_unique(Args&&... args)
{
   return std::unique_ptr<T> {new T {args...}};
}   // make_unique()

“多态层次”模式的解释如下:
1)我们知道所有的引擎都有不同的类型,但界面相同。

2)我们创建了一个抽象基类(RndBase),它包含了这个接口。它还定义了一个名为eng()的参数化(静态)工厂方法,该方法创建参数Eng的对象并返回对它的引用。预计将使用引擎作为参数。

3)我们创建了一个名为RndSub的参数化子类,它派生自基类RndBase。该类定义了一个调用运算符,它返回通过调用分布获得的随机数。

4)有效地,我们所做的如下:
   a)异构引擎由参数化子类 RndSub 抽象。每个子类都不同    b)但是,它们现在只有一个共同的基类 RndBase    c)由于只有一个基类( RndBase ),我们现在可以创建一个vector<RndBase>,它是同类的。 RndBase 的子类是异构的    d)由于接口是通用的,我们可以使用基类中定义的接口来调用子类中的实现。此实现在基类中调用工厂方法 eng ()以获取引擎,该引擎作为参数传递给分发。这将返回随机数。

此解决方案专门用于随机数。我正在尝试为任何类(具有类似接口)创建解决方案。