如何将C++11随机数生成器传递给一个函数?

21

它们都从一个基类继承吗?我必须使用模板吗?

(我指的是这些http://www.codeguru.com/cpp/cpp/cpp_mfc/stl/article.php/c15319/

我现在正在做这个:

typedef std::mt19937 RNG;

然后

class Chooser {
public:
    Chooser(RNG& rng, uint n, uint min_choices, uint max_choices):

换句话说,我正在传递对RNG的引用。那么我如何传递一个任意的生成器?
另外,我意识到这可能是一个不同的问题,但我如何将生成器传递给STL?
std::random_shuffle(choices_.begin(), choices_.end(), rng);

似乎无法工作。


通过生成器的解决方案:

typedef std::ranlux64_base_01 RNG;
typedef std::mt19937 RNGInt;

通过STL传递的解决方案:

struct STL_RNG {
    STL_RNG(RNGInt& rng): gen(rng) {}       
    RNGInt& gen;
    int operator()(int n) { return std::uniform_int<int>(0, n)(gen); }
};

根据那篇文章,它们已经是函子了,所以你应该准备好了。问题出在哪里? - Potatoswatter
如果我们知道你的目标,我们可能能够提供更具体和有用的信息。 - GManNickG
具体来说,您希望生成器有何不同,并且random_shuffle的编译器错误是什么? - Potatoswatter
一个函数对象是一个对象,但它的RNG是一种类型。RNG()将是一个(临时)函数对象。 - MSalters
使用您的想法,我组合了一个简单的解决方案。 - Neil G
4个回答

17

虽然它有点令人惊讶,但并非所有的functor都从一个基类继承(which is a little surprising)。但这没关系,因为这不是C++ functors的工作方式。

对于单个给定类型的任意RNGs,您现在已经正确了。

如果您的意思是,如何定义一个接受任何随机数生成器作为参数的函数。

template< class RNG > // RNG may be a functor object of any type
int random_even_number( RNG &gen ) {
    return (int) gen() * 2;
}

由于类型推导,您不需要使用比此更多的任何模板。


定义一个函数以接受不同的RNG会更加棘手,因为从语义上讲,这需要具有共同的基础类型。您需要定义一个基础类型。

struct RNGBase {
    virtual int operator() = 0;
    virtual ~RNGBase() {};
};

template< class RNG >
struct SmartRNG : RNGBase {
    RNG gen;

    virtual int operator() {
        return gen();
    }
};

int random_even_number( RNGBase &gen ) { // no template
    return (int) gen() * 2; // virtual dispatch
}

这真的很不幸,因为这意味着我必须将所有随机函数放在头文件中。 - Neil G
@Neil: 看看我的第二个答案(一开始我只提交了第一段)。我认为标准的意图是让你选择一个生成器并坚持使用它。如果你只想使用std::mt19937,那么没有必要进行任何操作...但这从你的问题中并不清楚。 - Potatoswatter
你说得对。感谢你的其余回答。如果这使代码更易处理,我很乐意将自己限制在mt上。 - Neil G
1
如果你要回答关于现代C++的问题,也许不要使用(int)作为强制类型转换语法? - Victor Eijkhout
@VictorEijkhout 先生,这篇帖子已经有12年的历史了。 - Potatoswatter
1
我注意到了。但既然它仍然出现在首页上,也许你可以进行一些小的编辑?人们确实会查阅旧答案。而C++11仍然是现代C++的基础,所以你的答案实际上仍然相关。 - Victor Eijkhout

9

对我有用的是使用 std::function

#include <functional>
#include <random>

void exampleFunction(std::function<int()> rnd) {
    auto randomNumber = rnd();
}

std::minstd_rand rnd;
exampleFunction([&rnd](){ return rnd(); });

// This won't work as it passes a copy of the object, so you end up with the same
// sequence of numbers on every call.
exampleFunction(rnd);

你实际上并没有传递随机对象,只是一个调用对象的operator ()方法的方法,但它达到了相同的效果。

请注意,由于std::function被声明为返回int,因此随机数生成器返回值的精度可能会降低,因此根据您对精度的需求,您可能需要使用不同的数据类型而不是int


0
将其包装在适合您需求的类或函数对象中?

有没有自动化的方法将类似这样的东西包装在一个函数对象中? - Neil G

0
我建议使用两种方法:函数对象和函数指针。在任一情况下,都应使您的类能够接收随机数生成器的函数对象或函数指针。
使用函数对象,您可以定义一个基类,并让接收类实现需要基函数对象类指针的函数。这样,即使不更改接收类的接口,也能自由定义许多不同的函数对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接