2

我想编写一个函数模板,它返回各种类型的随机变量(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 下,这给出了:

错误 C2668: '`anonymous-namespace'::randomPrimitive' : 对重载函数的模糊调用

当我尝试编译时:

randomPrimitive<float>();

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

4

3 回答 3

12

您正在接近您的编辑。您需要将“积分”版本限制为积分类型。这将消除歧义:

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

我注意到所有 10 个值signed char都是正的。那看起来不对。事实证明,它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

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

于 2013-08-20T02:27:57.493 回答
1

您的模板专业化语法不正确。试试这个:

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

区别在于<bool>函数名randomPrimitive和参数列表之间()


类型char可以是unsigned charsigned char- 这取决于编译器。您的实现显然取决于char被解释为signed char,因此您应该明确说明。

此外,表达式x ? true : falsewhere xis abool与 simple 相同x

于 2013-08-19T21:40:02.243 回答
1

您没有专门化(完全专业化除外),而是重载了函数模板。重载仅适用于参数类型,而不适用于结果类型。而且由于您的函数模板不接受任何参数,因此重载决议无法决定为您选择什么。

在这种情况下,您要么必须指定模板参数,要么尝试使用转换运算符:

struct RandomPrimitive
{
    operator float()
    {
        // your float implementation here
    }

    operator int()
    {
        // your int implementation here
    }

    // more operator type() as needed
};

RandomPrimitive randomPrimitive;

float f = randomPrimitive;
int i = randomPrimitive;
于 2013-08-19T21:28:27.247 回答