tl;dr:给定一个数字类型 T,是否有一种简洁的方法将变量声明为std::uniform_int_distribution<T>
或std::uniform_real_distribution<T>
取决于 T 是整数还是浮点数?
我需要生成std::chrono::duration
在调用者定义的范围内均匀分布的随机数,因此我创建了一个uniform_duration_distribution
以标准库的分布类模板为模型的类模板。
首先,我写了一个概念来将我的分布限制在一个时间持续时间内(或适当相似的类型)。
// Is T similar enough to std::chrono::duration for our purposes?
template <typename T>
concept chrono_duration = requires (T d) {
{ d.count() } -> std::same_as<typename T::rep>;
{ d.zero() } -> std::same_as<T>;
{ d.max() } -> std::same_as<T>;
};
持续时间有一个称为计数的数字表示。我的类包含标准库中的数字均匀分布,使用它来生成计数,并根据该计数构造持续时间。
template<chrono_duration DurationT>
class uniform_duration_distribution {
// ...
private:
using rep = typename DurationT::rep;
std::uniform_distribution<rep> m_distribution; // Whoops!
};
问题就在这里。持续时间计数的类型可以是整数类型,也可以是浮点类型,因此的类型m_distribution
并不像 这么简单,std::uniform_distribution<T>
因为没有这样的模板。
我不想对我的类进行多次特化,也不想将调用者限制在持续时间的一个特定实例上。我只想根据持续时间的表示类型选择包含分布的类型。
我的第一次尝试是使用受概念限制的类型别名模板。
template <std::integral IntT>
using dist_selector = std::uniform_int_distribution<IntT>;
template <std::floating_point FloatT>
using dist_selector = std::uniform_real_distribution<FloatT>;
这似乎不允许。我可以(显然)用一个概念来约束单个使用别名模板,但我不能使用概念在不同的别名之间进行选择。至少,我试过不行。有办法吗?
我还了解到我无法使用别名模板进行专门化。
最后我制作了一个针对数字类型的专门结构模板。
// Select the appropriate distribution type based on the value type.
template <typename T> struct dist_selector {};
template <> struct dist_selector<long double> { using t = std::uniform_real_distribution<long double>; };
template <> struct dist_selector<double> { using t = std::uniform_real_distribution<double>; };
template <> struct dist_selector<float> { using t = std::uniform_real_distribution<float>; };
template <> struct dist_selector<long long> { using t = std::uniform_int_distribution<long long>; };
template <> struct dist_selector<long> { using t = std::uniform_int_distribution<long>; };
template <> struct dist_selector<int> { using t = std::uniform_int_distribution<int>; };
template <> struct dist_selector<short> { using t = std::uniform_int_distribution<short>; };
template <> struct dist_selector<unsigned long long> { using t = std::uniform_int_distribution<unsigned long long>; };
template <> struct dist_selector<unsigned long> { using t = std::uniform_int_distribution<unsigned long>; };
// ...
那么成员变量定义为:
using rep = typename DurationT::rep;
using dist_type = typename dist_selector<rep>::t;
dist_type m_distribution;
这可行,但感觉像回到了旧方法。我是否错过了一种更现代的方法来实现这一点?
我将从类型特征/概念开始,将模板参数约束为某种
std::chrono::duration
类型:然后分布选择器可能看起来像这样,其中一个实例
T
必须与受约束的函数之一匹配test
:综合起来:
演示
您可以使用类模板通过概念进行专业化,然后为方便起见添加别名模板:
现场演示
或者你也可以使用
std::conditional
:现场演示
注意如何
std::type_identity
避免询问::type
不存在的成员别名(例如,std::type_identity<std::uniform_int_distribution<double>>
是“ok”类型,它只是没有type
成员别名)。