5

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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
cxxl
  • 4,939
  • 3
  • 31
  • 52
  • 2
    @juanchopanza … which comes at the cost of some overhead. – Konrad Rudolph Jan 08 '14 at 17:03
  • Looks like an MSVC bug. – chris Jan 08 '14 at 17:06
  • 3
    FYI, `bind` will create copies of its arguments, so if you were to create, say, `dice2`, it would use a different distribution and RNG than `dice`. To avoid this you'll need to wrap the arguments in [`reference_wrapper`](http://en.cppreference.com/w/cpp/utility/functional/ref) – Praetorian Jan 08 '14 at 17:30
  • What does the standard say about default and/or copy construction of the type returned from `std::bind`? EDIT: 20.8.9.1.2/5 "*Remarks*: The return type shall satisfy the requirements of `MoveConstructible`." – Casey Jan 08 '14 at 17:35
  • @chris It is far from obvious. What's the meaning of `distribution` and `generator` inside the `decltype`? To what *entities* do they refer? – n. m. could be an AI Jan 08 '14 at 18:01
  • @n.m., I'm not sure what the standard says about it, but Clang compiles it and it's not unreasonable to assume Clang wins when it's up against MSVC. – chris Jan 08 '14 at 18:04
  • 2
    @chris OK, found it. It's legal in C++11. Non-static data members may appear in an *unevaluated operand*. – n. m. could be an AI Jan 08 '14 at 18:29

3 Answers3

7

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.

Casey
  • 41,449
  • 7
  • 95
  • 125
  • But the point of `std::bind` is … *gasp* … to make this unnecessary. (Not that I think this code is a bad solution per se …) – Konrad Rudolph Jan 08 '14 at 17:11
  • @KonradRudolph: I don't really think that is the point of bind. The point of bind, as far as I can tell, is to allow one to not have to write one-off functions or functors, separated from their point of usage. It doesn't really fulfill that role in this case. – Benjamin Lindley Jan 08 '14 at 17:18
  • I'm not sure I know what the point of `bind` is now that we have lambdas in the language. Generic lambdas in C++14 will only make things worse for poor `bind`. I admit that for this specific case, `bind(distribution, generator)` is more terse than `[this]{return distribution(generator);}`. – Casey Jan 08 '14 at 17:27
  • 1
    It may save you a few characters there, but I would still prefer the lambda. Lambda's are, in my opinion, much easier to understand than bind, because they take a form similar to normal functions, and we write functions constantly. – Benjamin Lindley Jan 08 '14 at 17:36
  • @BenjaminLindley Agree completely: `bind` is effectively a mini-DSL that I have to think about instead of simply reading like any other C++ code. Lambda wins out in the end for terseness as well: `bind(std::ref(distribution), std::ref(generator))` vs. `[this]{return distribution(generator);}`. – Casey Jan 08 '14 at 17:50
  • @Casey: I used your first solution and it works fine and fast, thanks. The second one (with the std::ref()) gives compiler errors (could not deduce template argument...). From a practical standpoint, my problem is solved. Great work, guys! :) – cxxl Jan 08 '14 at 18:05
0

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).

zneak
  • 134,922
  • 42
  • 253
  • 328
  • But `decltype` *is* using type inference, no? – Konrad Rudolph Jan 08 '14 at 17:17
  • Apologies, yes. Let me test a bit further then. Note however that Microsoft's implementation of variadic templates was broken and hackish until VS2013, so it could be a bug that `std::function` will almost certainly work around. – zneak Jan 08 '14 at 17:21
  • Microsoft's implementation of variadic templates was _nonexistent_ until VC++ 2013. ;-] – ildjarn Jan 08 '14 at 20:21
  • I know, but they had ugly ugly hacks to support several of the methods that would require variadic parameters. – zneak Jan 08 '14 at 22:52
0

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.

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214