使用可变参数模板的随机选择器功能 - 是否可能?

s-v*_*s-v 11 c++ variadic-functions variadic-templates c++11

我想使用C++ 11的可变参数模板来实现通用的"随机选择器"功能.

像这样......

template <typename T>
T randomPicker(T one, T two, T three)
{
    int pick = 3 * (rand() / double(RAND_MAX));
    switch (pick)
    {
        case 0:
            return one;
        case 1:
            return two;
        default:
            return three;
    }
}
Run Code Online (Sandbox Code Playgroud)

...除了通用以接受任何数量的参数(每个参数类型相同,如上所述 - 尽管接受任何类型作为参数并且在返回时将所选类型转换为某个特定类型T也是可接受的).

我理解使用模板递归来实现类型安全printf等的想法.可变参数模板也可以用来创建上面描述的那种函数吗?任何提示赞赏!

Ste*_*sop 8

这样的东西,虽然我无法测试它:

template <typename First, typename... Others>
First randompicker(First first, Others ...args) {
    const size_t len = sizeof...(args) + 1;
    if (rand() / double(RAND_MAX) < 1.0 / len) {
        return first;
    }
    return randompicker(args...);
}

template <typename Only>
Only randompicker(Only only) {
    return only;
}
Run Code Online (Sandbox Code Playgroud)

我不确定那里的重载是否正确 - 可能是参数包可能是空的,我不知道我是否仍然可以在没有歧义的情况下为一个参数重载.

不可否认,这比使用3个args的示例使用更多随机数,并且可能对舍入误差的偏差更敏感.所以,你可以从选择一个随机数0,以len-1在开始,然后再打一个递归函数,选择n个参数进行参数包:

template <typename First, typename... Others>
First select(size_t idx, First first, Others ...args) {
    if (idx == 0) return first;
    return select(idx-1, args...);
}

template <typename Only>
Only select(size_t, Only only) {
    return only;
}

template <typename First, typename... Others>
First randompicker(First first, Others ...args) {
    static std::default_random_engine re;

    const size_t len = sizeof...(args) + 1;
    std::uniform_int_distribution<size_t> range{0, len - 1};

    const size_t idx = range(re);
    return select(idx, first, args...);
}
Run Code Online (Sandbox Code Playgroud)

在所有情况下,我都有nif/else语句而不是n-way开关.你可能会幸运的优化,或者您可能能够通过具有"展开循环"了一下First,Second... A few变量ARGS前参数ARGS.

  • `你可以在开始时选择一个随机数......然后......选择第n个参数` - >这将是我的偏好:清晰的语义,易于理解.我会为它采取额外的代码 (2认同)

Ara*_*raK 8

我不确定你是否必须使用可变参数模板,但恕我直言,它更容易使用初始化列表.

#include <cstddef>
#include <iostream>
#include <random>
#include <algorithm>
#include <initializer_list>
using namespace std;

template <class T>
T random_picker(initializer_list<T> container){
    static default_random_engine re;
    uniform_int_distribution<size_t> range{0, container.size()-1};
    auto random_iterator = container.begin();
    advance(random_iterator, range(re));
    return *random_iterator;
}

int main(){
    for(size_t i = 0; i < 10; ++i)
        cout << random_picker({1, 2, 3, 4, 5, 6, 7, 8, 9, 10}) << endl;
}
Run Code Online (Sandbox Code Playgroud)