【问题标题】:Passing arbitrary random number distributions to a function将任意随机数分布传递给函数
【发布时间】:2020-09-17 15:55:34
【问题描述】:

我正在编写一个矩阵初始化方法,该方法用用户可以选择的随机数分布中的 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);
*/

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

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

【问题讨论】:

  • 嗯,到底是什么问题?

标签: c++ variables random std


【解决方案1】:

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

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

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 (,,) 或 concepts () 进一步限制输入,使其仅适用于分布。这将确保调用 init_Random 时不会考虑重载解决方案。

SFINAE 方法

引入了std::is_invocable_r,它可以与std::enable_if 结合使用,仅当dist 可正确调用并返回可转换为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&amp; 调用,并返回可转换为矩阵基础类型T 的类型。 enable_if 将确保此函数只能由可以调用 RandomNumberDistribution 的任何输入调用。

如果您低于,则需要实现自定义is_invocable_r 特征,或使用开源库中现成的特征——但这仍然与早期的C++ 版本兼容.

概念方法

如果你在 或以上,你可以用概念来限制它。有一个 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)

【讨论】:

  • 这是一个非常全面的答案,我相信它可以解决我想要的一切。非常感谢。我会从中学到很多东西。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2017-12-18
  • 2018-12-12
  • 1970-01-01
  • 1970-01-01
  • 2020-03-12
  • 2015-12-23
相关资源
最近更新 更多