2

我正在尝试std::discrete_distribution使用传递给类构造函数的数据创建一个对象。我知道如何使用静态数据创建它,但无法弄清楚如何使用可变数据(干净地)。我现在所拥有的“有效”,但很痛苦。有没有更合适的方法来做到这一点?

distInit = { distArray[0], ... };线是问题。

#include <iostream>
#include <iomanip>
#include <initializer_list>
#include <map>
#include <random>

class Die {
    private:
        int loadSide;
        double loadAmount;
        std::mt19937 generator;
        std::discrete_distribution<> distribution;
        std::initializer_list<double> distInit;
        std::array<double, 7> distArray;
    public:
        Die( int loadSide, double loadAmount ) : loadSide(loadSide), loadAmount(loadAmount) {
            distArray.fill( 1 );
            distArray[0] = 0;
            distArray[this->loadSide] = this->loadAmount;

            distInit = { distArray[0], distArray[1], distArray[2], distArray[3], distArray[4], distArray[5], distArray[6] };
            distribution.param( distInit );
        };
        int roll( ) {
                return distribution( generator );
        };
};

const int ROUNDS = 10000;

int main() {
    Die* die = new Die( 5, 20 );

    std::map<int, int> m;
    for(int n=0; n < ROUNDS; n++) {
        m[die->roll()]++;
    }
    for(auto p : m) {
        std::cout << p.first << " generated " << std::setiosflags(std::ios::fixed) << std::setprecision(2) << (float) p.second / ROUNDS << " times\n";
    }
}

我可能没有问正确的问题,如果是这样,我会提前道歉。这是一个很大的可能性,因为我很惊讶我在这个主题上找不到任何(显然)相关的命中。

我的编译器是g++-mp-4.8 (MacPorts gcc48 4.8-20130411_0) 4.8.1 20130411 (prerelease)

命令行/opt/local/bin/g++-mp-4.8 -std=c++11 test.cpp -o test

4

3 回答 3

2

std::initializer_list仅用作临时对象(函数参数)或局部变量。它不是一个容器,它不拥有任何东西;它是匿名临时数组的访问器。

该标准包括一个类似于您的代码 §8.5.4/6 的示例,其中提到

initializer_list 对象在构造函数的 ctor-initializer 中初始化,因此该数组仅在构造函数退出之前一直存在,因此在构造函数退出后对 i4 元素的任何使用都会产生未定义的行为。

在您的情况下,它是构造函数的主体,而不是主体之前的 ctor-initializer,但故事是一样的。您的程序现在正在运行只是运气不好。

要将分布存储在对象中,请使用std::arraystd::vectorarray效率更高,但不支持arr = { … }语法。(有一些简单的替代方案。)vector确实支持使用大括号和=运算符的语法;此支持使用隐式std::initializer_list.

于 2013-05-03T04:14:01.517 回答
2

如果您有可变数据,则应该使用discrete_distribution带有一对迭代器的构造函数

template< class InputIt >
discrete_distribution( InputIt first, InputIt last );

您不应该尝试param_type直接构建;而是使用辅助函数来构建您的分布:

class Die {
    private:
        std::mt19937 generator;
        std::discrete_distribution<> distribution;
        static std::discrete_distribution<> makeDistribution(
            int loadSide, double loadAmount )
        {
            std::array<double, 7> distArray;
            distArray.fill( 1 );
            distArray[0] = 0;
            distArray[loadSide] = loadAmount;
            return {std::begin(distArray), std::end(distArray)};
        }
    public:
        Die( int loadSide, double loadAmount ) :
            generator{ },
            distribution{ makeDistribution( loadSide, loadAmount ) }
        {}
        int roll( ) {
                return distribution( generator );
        }
};
于 2013-05-03T09:32:05.070 回答
0

除了 OP 中显示的容器之外,我不知道有什么更好的方法可以std::initializer_list从容器中创建容器。std::array

但是,对于最初的问题,即将参数传递给分布,我可以提出一些更简单的建议。

    typedef std::discrete_distribution<>::param_type param_type;
    distribution.param(param_type(distArray.begin(), distArray.end()));

该标准规定分发必须提供一个类型成员param_type(这是由 采用的参数类型param()),但没有指定它。然而,[rand.req.dist] 说

对于 D [分布类型] 的每个构造函数,其参数对应于分布的参数,P [param_type] 应具有相应的构造函数,该构造函数具有相同的要求并具有相同的数量、类型和默认值的参数。

好吧,事实证明std::discrete_distribution<>有一个构造函数采用指向参数范围的迭代器。因此,无论std::discrete_distribution<>::param_type是什么,它都必须有一个类似的构造函数。因此,我建议创建一个param_typefrom distArray.begin()anddistArray.end()并将其传递给distribution.param().

附注:您不再需要std::initializer_list<double> distInit;在课堂上。在我看来,您也不需要std::array<double, 7> distArray作为类成员(它可能是Die的构造函数中的局部变量)。

于 2013-05-03T04:04:07.240 回答