首页 > 解决方案 > 模板类中的动态模板方法选择

问题描述

我想创建一个模板随机数生成器类,它可以是整数类型或浮点类型。为什么?对于作业,我编写了一个累积函数(基本上与 std::accumulate 相同),并且我想制作一个可以是任何整数或浮点类型的测试工具(例如,无符号|短|长|长整型、浮点型、双倍的)。我们一直在研究模板,我正在尝试使用模板编程来做出动态的编译时决策。我可能以错误的方式处理这个问题 - 非常感谢任何建议/参考。

这是我的测试功能:

void testdrive() {
    std::vector<int> vint(ELEMENTS);
    std::vector<double> vflt(ELEMENTS);
    RNG<int> intrng;
    RNG<double> fltrng;

    std::generate(vint.begin(), vint.end(), intrng)
    std::generate(vflt.begin(), vflt.end(), fltrng)

    std::cout << "Sum of " << printvec(vint) << "is " accum(vint) << "\n\n";
    std::cout << "Sum of " << printvec(vflt) << "is " accum(vflt) << '\n';
}

我不知道如何为我的班级使用模板编程来做到这一点。我想要做的是,如果类型是 int 类型,则使用 uniform_int_distribution,如果是 float|double,则使用 uniform_real_distribution。我意识到这两者并不完全可以互换,但对于我正在尝试做的事情来说这很好。这是我的课:

template<typename T>
class RNG {
    public:
        RNG(T low=std::numeric_limits<T>::min(),
            T high=std::numeric_limits<T>::max())
            : rng_engine{rng_seed()}, rng_dist{low, high}, rng_low{low},
              rng_high{high} { }
        RNG(const RNG& r): rng_engine{rng_seed()}, rng_dist{r.rng_low,
            r.rng_high}, rng_low{r.rng_low}, rng_high{r.rng_high} { }
        T operator()() { return rng_dist(rng_engine); }
    private:
        std::random_device rng_seed;
        std::mt19937 rng_engine;
        template<typename U, typename=std::enable_if_t<std::is_integral<T>::value>>
            std::uniform_int_distribution<T> rng_dist;
        template<typename U, typename=std::enable_if_t<std::is_floating_point<T>::value>>
            std::uniform_real_distribution<T> rng_dist;
        T rng_low, rng_high;
};

此外,对于阅读这篇文章的任何人,我发现这本书对于深入了解 C++ 模板非常有帮助:C++ 模板 - 完整指南第 2 版 ( http://www.tmplbook.com/ )

标签: c++c++17

解决方案


看看模板专业化。在下面的代码中,私人struct Distribution选择std::uniform_*_distribution使用哪个。

#include <stdio.h>
#include <vector>
#include <algorithm>
#include <random>
#include <iostream>

template <class T>
class RNG
{
    // primary template is designed for integers
    template <class U>
    struct Distribution
    {
        typedef std::uniform_int_distribution<U> Type;
    };
    // explicit specialization for float
    template <>
    struct Distribution<float>
    {
        typedef std::uniform_real_distribution<float> Type;
    };
    // explicit specialization for double
    template <>
    struct Distribution<double>
    {
        typedef std::uniform_real_distribution<double> Type;
    };

    std::random_device rng_source;
    typename Distribution<T>::Type rng_dist;

public:
    RNG(
        T low = std::numeric_limits<T>::min(),
        T high = std::numeric_limits<T>::max())
        : rng_source{}
        , rng_dist(low, high)
    {
    }
    RNG(const RNG& rhs)
        : rng_source{}
        , rng_dist(rhs.rng_dist)
    {
    }

    T operator()()
    {
        return rng_dist(rng_source);
    }
};

int main()
{
    const size_t ELEMENTS = 10;
    std::vector<int> vint(ELEMENTS);
    std::vector<double> vflt(ELEMENTS);
    RNG<int> intrng(0, 100);
    RNG<double> fltrng(0.0, 1.0);

    std::generate(vint.begin(), vint.end(), intrng);
    std::generate(vflt.begin(), vflt.end(), fltrng);

    return 0;  <-- set a breakpoint here to see both vectors
}

推荐阅读