专门针对bool+积分+浮点返回类型的模板

specializing a template on bool + integral + floating-point return types

本文关键字:返回类型 bool+ 积分      更新时间:2023-10-16

我想写一个函数模板,它返回各种类型的随机变量(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:"匿名命名空间"::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

如果这仍然没有输出你想要的,希望你有足够的方向来实现它。

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

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

不同之处在于函数名称randomPrimitive和参数列表()之间的<bool>


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

此外,其中xbool表达式的表达式x ? true : false与简单的x相同。

您不进行专门化(完全专门化除外),而是重载函数模板。重载只对参数类型有效,对结果类型无效。由于您的函数模板不接受任何参数,因此过载解析无法决定为您选择什么。

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

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;