Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can not capture a random distribution and generator by value in lambda?

Tags:

c++

c++11

lambda

The following code works fine

#include <iostream>
#include <random>

int main() {
    std::default_random_engine generator;
    std::normal_distribution<double> distribution(5.0, 2.0);
    std::vector<double> v(100);

    std::generate(v.begin(), v.end(), [&] () { return distribution(generator); });

    return 0;
}

However, changing the capture list of the lambda to [=] or [&, distribution] or [&, generator] causes

rand.cpp:9:59: error: no matching function for call to object of type 'const std::normal_distribution<double>'
error: assigning to 'double' from incompatible type 'void'

Are there some kinds of objects can not be captured by value in a lambda ?

like image 787
Saddle Point Avatar asked Aug 28 '17 06:08

Saddle Point


2 Answers

Looks like std::normal_distribution<double>::operator() is not const. So you have to add mutable keyword to your lambda:

std::generate(v.begin(), v.end(), [=] () mutable { 
   return distribution(generator); 
});

std::generate(v.begin(), v.end(), [&, distribution] () mutable { 
   return distribution(generator); 
});

These both work fine.

like image 167
WindyFields Avatar answered Nov 13 '22 06:11

WindyFields


In general, capture by values requires the object to be copyable. Both std::default_random_generator and std::normal_distribution are copyable, so this shouldn't be a problem here.

Lambda captures are always const values or const references. Random number generation is inherently stateful, so the called operator() cannot be const because it may modify the object state. The error message cryptically reports this by saying that there is no function to call on a const std::normal_distribution<double>.

If you want to modify the captured objects, you have to declare the lambda as mutable

#include <iostream>
#include <random>
#include <algorithm>

int main() {
    std::default_random_engine generator;
    std::normal_distribution<double> distribution(5.0, 2.0);
    std::vector<double> v(100);

    std::generate(v.begin(), v.end(), [generator, distribution] () mutable { return distribution(generator); });

    return 0;
}
like image 38
Jens Avatar answered Nov 13 '22 06:11

Jens