c++ - 专门针对 bool + integral + floating-point 返回类型的模板

标签 c++ c++11

我想编写一个函数模板,它返回各种类型的随机变量(bool、char、short、int、float、double,以及这些类型的无符号版本)。

我不知道如何使用最新的 C++11 标准库来执行此操作,因为我需要使用 uniform_int_distribution 或 uniform_real_distribution。我以为我可以专门化模板:

template<typename T>
T randomPrimitive() { std::uniform_int_distribution<T> dst; std::mt19937 rng; return dst(rng); }

template<>
bool randomPrimitive<bool>() { std::uniform_int_distribution<signed char> dst; std::mt19937 rng; return dst(rng) >= 0 ? true : false; }

template<typename T>
typename std::enable_if<std::is_floating_point<T>::value, T>::type randomPrimitive() { std::uniform_real_distribution<T> dst; std::mt19937 rng; return dst(rng); }

在 Visual Studio 2012 Update 3 下,这给出了:

error C2668: '`anonymous-namespace'::randomPrimitive' : 对重载函数的调用不明确

当我尝试编译时:

randomPrimitive<float>();

有没有办法专门化一个函数模板,这样我就可以为 bool、其他整数类型和浮点类型编写三种不同的实现?

最佳答案

您即将完成编辑。您需要将“积分”版本限制为积分类型。这将消除歧义:

template<typename T>
typename std::enable_if
<
    std::is_integral<T>::value,
    T
>::type
randomPrimitive()

但是如果你现在用这样的东西运行它:

#include <iostream>

int
main()
{
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<float>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<double>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << (int)randomPrimitive<signed char>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<unsigned>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<bool>() << '\n';
}

你会得到类似的东西:

0.814724
0.814724
0.814724
0.814724
0.814724
0.814724
0.814724
0.814724
0.814724
0.814724
0.135477
0.135477
0.135477
0.135477
0.135477
0.135477
0.135477
0.135477
0.135477
0.135477
92
92
92
92
92
92
92
92
92
92
3499211612
3499211612
3499211612
3499211612
3499211612
3499211612
3499211612
3499211612
3499211612
3499211612
1
1
1
1
1
1
1
1
1
1

到达那里,但不是完全随机的。问题是你每次使用它时都在构建一个新引擎。您想要的是创建一次 URNG,然后不断从中获取随机位:

std::mt19937&
get_eng()
{
    static std::mt19937 eng;
    return eng;
}

而且您实际上也应该只创建一次您的发行版。他们中的大多数是无国籍的,但不是全部。最好只是假设它们都带有状态,并且您不想丢弃该状态。

static std::uniform_real_distribution<T> dst;

这将大大改善事情,但你还没有做到:

0.814724
0.135477
0.905792
0.835009
0.126987
0.968868
0.913376
0.221034
0.632359
0.308167
0.547221
0.188382
0.992881
0.996461
0.967695
0.725839
0.98111
0.109862
0.798106
0.297029
92
13
49
122
46
7
105
45
43
8
2816384844
3427077306
153380495
1551745920
3646982597
910208076
4011470445
2926416934
2915145307
1712568902
0
1
1
1
1
0
1
0
1
0

我注意到 signed char 中的所有 10 个值都是正数。那看起来不对。事实证明,std::uniform_int_distribution 有一个如下所示的构造函数:

explicit uniform_int_distribution(IntType a = 0,
                                  IntType b = numeric_limits<IntType>::max());

我猜这不是你想要的,所以:

static std::uniform_int_distribution<T> dst(std::numeric_limits<T>::min(), 
                                            std::numeric_limits<T>::max());

最后,如果您想要一个随机的 bool,请使用 std::bernoulli_distribution

把这些放在一起:

#include <random>

std::mt19937&
get_eng()
{
    static std::mt19937 eng;
    return eng;
}

template<typename T>
typename std::enable_if
<
    std::is_integral<T>::value,
    T
>::type
randomPrimitive()
{
    static std::uniform_int_distribution<T> dst(std::numeric_limits<T>::min(), 
                                                std::numeric_limits<T>::max());
    return dst(get_eng());
}

template<>
bool
randomPrimitive<bool>()
{
    static std::bernoulli_distribution dst;
    return dst(get_eng());
}

template<typename T>
typename std::enable_if
<
    std::is_floating_point<T>::value,
    T
>::type
randomPrimitive()
{
    static std::uniform_real_distribution<T> dst;
    return dst(get_eng());
}

#include <iostream>

int
main()
{
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<float>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<double>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << (int)randomPrimitive<signed char>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<unsigned>() << '\n';
    for (int i = 0; i < 10; ++i)
        std::cout << randomPrimitive<bool>() << '\n';
}

对我来说输出:

0.814724
0.135477
0.905792
0.835009
0.126987
0.968868
0.913376
0.221034
0.632359
0.308167
0.547221
0.188382
0.992881
0.996461
0.967695
0.725839
0.98111
0.109862
0.798106
0.297029
92
13
-79
-6
46
-121
-23
45
43
8
2816384844
3427077306
153380495
1551745920
3646982597
910208076
4011470445
2926416934
2915145307
1712568902
0
1
1
1
1
0
1
0
1
0

如果这仍然没有输出您想要的,希望您有足够的方向从这里开始。

关于c++ - 专门针对 bool + integral + floating-point 返回类型的模板,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18323372/

相关文章:

c++ - C++ 中是否有任何与传输无关的 JSON-RPC 实现?

c++ - 我的时间程序不会过载。为什么?

c++ - 如何获得给定进程的窗口站?

c++ - 为什么 std::condition_variable 不等待并立即再次获得锁定?

c++ - 如何在 C++11 中创建指向静态成员函数的指针映射?

c++ - regex_replace,为什么它会丢失 $1?

c++ - 转发引用 : returning T when given T&& and T& when given T&

c++ 将指向类的指针转换为类的实例

c++ - prvalue 表达式的 decltype 中的 cv 限定符

c++ - 为什么 b.isEm() 在不同的行打印不同的东西?