【问题标题】:specializing a template on bool + integral + floating-point return types专门针对 bool + 整数 + 浮点返回类型的模板
【发布时间】:2013-08-19 21:23:18
【问题描述】:

我想编写一个函数模板,它返回各种类型的随机变量(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、其他整数类型和浮点类型编写三种不同的实现?

【问题讨论】:

  • 语法错误——看我的回答
  • 您确定要为每个函数调用创建另一个 RNG 实例吗?通常您会创建一个 RNG 实例并将其用于许多调用(每个调用都会生成另一个随机数)。
  • 在这种情况下,我最好避免保持状态,每次都简单地实例化一个新的 RNG。
  • 顺便说一下,bool 的特化应该使用bernoulli_distribution()。你的版本总是返回true

标签: c++ c++11


【解决方案1】:

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

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

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

【讨论】:

  • 我终于有机会试一试了,它就像一个魅力。非常感谢您的详细协助!
【解决方案2】:

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

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

区别在于函数名称randomPrimitive和参数列表()之间的&lt;bool&gt;


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

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

【讨论】:

  • 感谢您的修复 - 您的版本也为我编译。我认为“对重载函数的歧义调用”错误来自其他两个声明之间的歧义,因此它不允许 randomPrimitive() 编译。
  • 啊哈好点@Timothy,我把它改成了“签名字符”。 x ? true : false 只是一种风格偏好。
【解决方案3】:

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

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

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;

【讨论】:

  • 我希望只编写三个实现,就像我在问题中的示例一样。对于转换运算符,我认为我必须为每种支持的类型(bool、char、unsigned char、int、float double 等)编写一个单独的转换运算符?指定模板参数很好,但我仍然无法编译 randomPrimitive()。
猜你喜欢
  • 2014-03-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多