简体   繁体   中英

How to std::bind() to create a data member?

I'm generating random values with C++11 nice new generators and distributions. In a function it works like a charm and looks like this:

void foo() {
   mt19937 generator;
   uniform_int_distribution<unsigned> distribution;
   auto dice = bind(distribution, generator);
   // dice() will now give a random unsigned value
}

But how can I put all three objects in a class as data members? I can simply write generator and distribution as data members, but how do I make dice a data member without knowing (or wanting to know) its exact type? Suprisingly this

class X {
   mt19937 generator;
   uniform_int_distribution<unsigned> distribution;
   decltype(bind(distribution, generator)) dice;
};

yields the error error C2660: 'bind' : function does not take 2 arguments in Visual Studio 2013.

You could always gasp write a function instead of using a lambda/bind/etc.:

class X {
   mt19937 generator;
   uniform_int_distribution<unsigned> distribution;
public:
   auto dice() -> decltype(distribution(generator)) {
     return distribution(generator);
   }
   // or alternatively
   auto operator() () -> decltype(distribution(generator)) {
     return distribution(generator);
   }
};

Bonus points for parameterizing on the type of the generator and/or distribution, and for holding the generator with a std::shared_ptr so that you can make several objects with differing distributions that share the same engine. You'll eventually want a constructor to seed the generator as well - Ideally with something like std::random_device{}() .

Or, the answer I think you are looking for :

class X {
   mt19937 generator{std::random_device{}()};
   uniform_int_distribution<unsigned> distribution{1,6};
public:
   decltype(bind(std::ref(distribution), std::ref(generator))) dice{
     bind(std::ref(distribution), std::ref(generator))
   };
};

I'm sorry I mocked you for trying to use bind in the first place: it's actually kind of neat that you can write this class with "no code" in C++11. We need to get type-inference for class member declarations in C++17 so this could be:

class X {
   auto generator = mt19937{std::random_device{}()};
   auto distribution = uniform_int_distribution<unsigned>{1,6};
public:
   auto dice = bind(std::ref(distribution), std::ref(generator));
};

Given that the latest Concepts Lite paper proposes using concept names anywhere in the language where auto can appear to mean "infer type, ill-formed if type doesn't model named concept," auto member declarations may not be out of the question.

The result of std::bind is unspecified: this means that you cannot store its raw result without type inference. However, you can use std::function to encapsulate the result of bind :

#include <functional>
std::function<unsigned()> dice(std::bind(distribution, generator));
auto result = dice();

EDIT: As whoever said above, this is most clearly a Visual Studio issue. I can confirm that this compiles with VS2013:

#include <functional>
#include <random>

using namespace std;

class X {
    mt19937 generator;
    uniform_int_distribution<unsigned> distribution;
    std::function<unsigned()> dice;

public:
    X() : dice(bind(distribution, generator)) {}

    unsigned roll() { return dice(); }
};

but changing the type of dice to decltype(bind(distribution, generator)) makes the whole thing fail with flying colors (even though it still works with clang).

It works on GCC . I'm pretty sure that's just a compiler bug. Unfortunately this means that you have to bite the bitter pill and use one of the workarounds described in the other answers.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM