Why doesn't creating a temporary instance work in this case?

I am learning C ++ 11 through Bjarne Stroustrup's "C ++ Journey".

I have the following piece of code

#include <random>
#include <functional>

int main(int argc, char *argv[])
{
    using namespace std;
    std::default_random_engine generator;
    std::uniform_int_distribution<int> distribution {1,6};
    distribution(generator); //Case 1 works

    auto die = bind(uniform_int_distribution<>{1,6},default_random_engine{});  //Case 2 works

    distribution(std::default_random_engine{});     //Case 3 Compiler error 
}

      

Case 3 is my own creation, while Case 2 is from the book and Case 1, which I adapted from other sources. Why is case 3 generating below compiler error? As I understand it, the only difference between case 1 and case 3 is that I am using a temporary std :: default_random_engine instance and this temporary instance seems to work in case 2 What am I missing?

Error output:

random.cpp: In function β€˜int main(int, char**)’:
random.cpp:13:46: error: no match for call to β€˜(std::uniform_int_distribution<int>) (std::default_random_engine)’
     distribution(std::default_random_engine{});     //Case 3 Compiler error
                                              ^
In file included from /usr/include/c++/6.3.1/bits/random.h:35:0,
                 from /usr/include/c++/6.3.1/random:49,
                 from random.cpp:1:
/usr/include/c++/6.3.1/bits/uniform_int_dist.h:164:2: note: candidate: std::uniform_int_distribution<_IntType>::result_type std::uniform_int_distribution<_IntType>::operator()(_UniformRandomNumberGenerator&) [with _UniformRandomNumberGenerator = std::linear_congruential_engine<long unsigned int, 16807ul, 0ul, 2147483647ul>; _IntType = int; std::uniform_int_distribution<_IntType>::result_type = int] <near match>
  operator()(_UniformRandomNumberGenerator& __urng)
  ^~~~~~~~
/usr/include/c++/6.3.1/bits/uniform_int_dist.h:164:2: note:   conversion of argument 1 would be ill-formed:
random.cpp:13:23: error: invalid initialization of non-const reference of type β€˜std::linear_congruential_engine<long unsigned int, 16807ul, 0ul, 2147483647ul>&’ from an rvalue of type β€˜std::default_random_engine {aka std::linear_congruential_engine<long unsigned int, 16807ul, 0ul, 2147483647ul>}’
     distribution(std::default_random_engine{});     //Case 3 Compiler error
                       ^~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/include/c++/6.3.1/bits/random.h:35:0,
                 from /usr/include/c++/6.3.1/random:49,
                 from random.cpp:1:
/usr/include/c++/6.3.1/bits/uniform_int_dist.h:169:2: note: candidate: template<class _UniformRandomNumberGenerator> std::uniform_int_distribution<_IntType>::result_type std::uniform_int_distribution<_IntType>::operator()(_UniformRandomNumberGenerator&, const std::uniform_int_distribution<_IntType>::param_type&) [with _UniformRandomNumberGenerator = _UniformRandomNumberGenerator; _IntType = int]
  operator()(_UniformRandomNumberGenerator& __urng,
  ^~~~~~~~
/usr/include/c++/6.3.1/bits/uniform_int_dist.h:169:2: note:   template argument deduction/substitution failed:
random.cpp:13:46: note:   candidate expects 2 arguments, 1 provided
     distribution(std::default_random_engine{});     //Case 3 Compiler error
                                              ^

      

+3


source to share


1 answer


distribution(generator);

      

distribution

changes state generator

and retrieves value. On next call, it returns a different value using the samegenerator

auto die = bind(uniform_int_distribution<>{1,6},default_random_engine{});

      

Here we copy both uniform_int_distribution<>{1,6}

and default_random_engine{}

into the bind function object. On ()

he transfers default_random_engine{}

in uniform_int_distribution<>{1,6}

via operator()

. uniform_int_distribution<>{1,6}

changes state default_random_engine{}

and on next call, it returns a different value since it uses a generator (now modified).

distribution(std::default_random_engine{});

      

here you are trying to bind random_engine&

to a temporary object. distribution

wants to change the random state of the engine, so it is taken by reference.

Passing the temporary, any such change will be immediately discarded. It's easy to accidentally do this and mess up, so by default C ++ doesn't allow you to bind a temporary object to a non-const lvalue reference.

The C ++ std designers wanted this bug, so they accepted their propagation over non-const lvalue reference. He is trying to tell you, "This is a bad plan."



You can get around this:

template<class T>
T& as_lvalue(T&& t){ return t; }

      

Now

distribution(as_lvalue(std::default_random_engine{}));

      

tricks the compiler into thinking the passed default_random_engine

one is not temporary. It still leaves at the end of the line, and the generator state change carefully done by allocation is discarded.

This is usually not what you want when generating pseudo-random objects, which is what the C ++ 11 random engine is.

In short, you are not allowed to bind temporary references to non-persistent lvalue references. When you do this, it is a mistake. When you have a parameter that is read / write, and the written value is important, it is taken with a reference to an lvalue constant so that you do not accidentally pass by the temporary and write data to it lost.

Stateful generators. You want to save it and reapply the distribution to it.

+2


source







All Articles