将任意随机数分布传递给函数
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);
...
};
通过将其设为 template
,init_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 (c++11,c++14,c++17), or concept
s (c++20) 将输入进一步限制为仅适用于分布。这将确保使用不起作用的东西调用 init_Random
不会被考虑用于重载决议。
SFINAE 方法
c++17 引入了 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 类型可转换为矩阵的基础类型,T
。 enable_if
将确保此函数只能由任何可以调用 RandomNumberDistribution
的输入调用。
如果您低于 c++17,您将需要实施自定义 is_invocable_r
特征,或使用 open-source 库中的 off-the-shelf 特征——但是这仍然与早期的 C++ 版本兼容。
概念方法
如果您处于 c++20'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)
我正在编写一个矩阵初始化方法,用用户可以选择的随机数分布中的 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);
...
};
通过将其设为 template
,init_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 (c++11,c++14,c++17), or concept
s (c++20) 将输入进一步限制为仅适用于分布。这将确保使用不起作用的东西调用 init_Random
不会被考虑用于重载决议。
SFINAE 方法
c++17 引入了 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 类型可转换为矩阵的基础类型,T
。 enable_if
将确保此函数只能由任何可以调用 RandomNumberDistribution
的输入调用。
如果您低于 c++17,您将需要实施自定义 is_invocable_r
特征,或使用 open-source 库中的 off-the-shelf 特征——但是这仍然与早期的 C++ 版本兼容。
概念方法
如果您处于 c++20'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)