将任意随机数分布传递给函数

Passing arbitrary random number distributions to a function

我正在编写一个矩阵初始化方法,用用户可以选择的随机数分布中的 T 型随机数填充 T 型矩阵。我的实现文件部分看起来像

// file name: Matrix.h
#include <random>

typedef std::default_random_engine rnd_eng;

template<class T, int N, int M>
class Matrix{
  private:
    /* private members here */
  public:
    Matrix<T, N, M>();
    /* other constructors */
    ~/Matrix<T, N, M>();

    void init_all(T value); // initializes all elements of Matrix NxM to value
    void init_Random(rnd_eng, USER RANDOM NUMBER DISTRIBUTION HERE);
};

/*
  Ideally I would have something that looks like the following in a main.cpp file
    rnd_eng engine(1);
    std::uniform_real_distribution real_dist(1.0f, 1.0f);
    std::uniform_int_distribution int_dist(1, 9);

    Matrix<float, 32, 64> my_mat_a;
    Matrix<int, 128, 256> my_mat_b;

    my_mat_a.init_Random(engine, real_dist);
    my_mat_b.init_Random(engine, int_dist);
*/

我想知道用什么来代替“此处的用户随机号码分配”。你们有什么建议吗?如果这是一个基本问题,我们深表歉意。

** 编辑:希望让我的问题更清楚一些

您要的是一个接受多种不同底层对象类型(生成器)的函数,这些对象(生成器)共享一个共同的签名,但没有层次结构。

这是一个最容易用模板解决的问题:

template<class T, int N, int M>
class Matrix{
    ...
    template <typename RandomNumberDistribution>
    void init_Random(rnd_eng, RandomNumberDistribution& dist);
    ...
};

通过将其设为 templateinit_Random 可以使用任何输入进行分发。

这将为您提供所需的语法:

    rnd_eng engine(1);
    std::uniform_real_distribution real_dist(1.0f, 1.0f);
    std::uniform_int_distribution int_dist(1, 9);

    Matrix<float, 32, 64> my_mat_a;
    Matrix<int, 128, 256> my_mat_b;

    // calls template with RandomNumberDistribution = std::uniform_real_distribution 
    my_mat_a.init_Random(engine, real_dist); 
    // calls template with RandomNumberDistribution = std::uniform_int_distribution 
    my_mat_b.init_Random(engine, int_dist);

还可以通过使用 SFINAE (,,), or concepts () 将输入进一步限制为仅适用于分布。这将确保使用不起作用的东西调用 init_Random 不会被考虑用于重载决议。

SFINAE 方法

引入了 std::is_invocable_r,它可以与 std::enable_if 结合使用,仅在 dist 可正确调用且 return 是一个类型时才启用此重载可转换为 T:

    template <typename RandomNumberDistribution,
              typename = std::enable_if_t<std::is_invocable_r_v<T,RandomNumberDistribution,rnd_eng&>>>
    void init_Random(rnd_eng, RandomNumberDistribution& dist)

使用 is_invocable_r 可用于测试输入 RandomNumberDistribution 必须可从 rnd_eng& 调用并且 return 类型可转换为矩阵的基础类型,Tenable_if 将确保此函数只能由任何可以调用 RandomNumberDistribution 的输入调用。

如果您低于 ,您将需要实施自定义 is_invocable_r 特征,或使用 open-source 库中的 off-the-shelf 特征——但是这仍然与早期的 C++ 版本兼容。

概念方法

如果您处于 's or above, you could constrain this with concepts. There is a std::invocable 概念中,而不是 invocable_with_result -- 那么您必须自己制作一个:

template<typename R, typename F, typename... Args>
concept invocable_with_result =
  requires(F&& f, Args&&... args) {
    static_cast<R>(std::invoke(std::forward<F>(f), std::forward<Args>(args)...));
  };

然后只需要在函数上设置要求 template:

    template <typename RandomNumberDistribution>
        requires(invocable_with_result<T, RandomNumberDistribution, rnd_eng&>)
    void init_Random(rnd_eng, RandomNumberDistribution& dist)