Лямбда-функция, которая является переменной-членом, вылетает - PullRequest
0 голосов
/ 03 июля 2018

Класс montecarlo содержит лямбду в качестве переменной-члена. Этот код можно скомпилировать, но он вызовет «Ошибка сегментации (ядро выгружено)» во время выполнения. Не могли бы вы объяснить, как это исправить?

#include<random>
#include<functional>
#include<iostream>

class montecarlo
{
  public:
    montecarlo(double x_min, double x_max);
    std::function<double()> rand;
};

montecarlo::montecarlo(double x_min, double x_max){
  std::random_device rd;
  std::mt19937 mt(rd());
  std::uniform_real_distribution<double> rand_(x_min, x_max); 
  rand = [&](){return rand_(mt);};
}

int main(){
  montecarlo x(0, 1);
  std::cout<<x.rand()<<std::endl;
}

И что меня удивило, так это когда я изменил реализацию конструктора на приведенный ниже код, он заработал:

montecarlo::montecarlo(double x_min, double x_max){
  rand = [](){return 0;};
}

Вы, наверное, знаете, но позвольте мне сказать, что я хочу не просто использовать случайные функции.

Ответы [ 3 ]

0 голосов
/ 03 июля 2018

Вы пытаетесь захватить rand_ и mt по ссылке; это локальные объекты внутри montecarlo::montecarlo, когда лямбда вызывается снаружи montecarlo::montecarlo, эти локальные объекты были уничтожены, а ссылки, хранящиеся в лямбда-объекте, стали висячими.

Вы можете изменить его на захват копии; и обратите внимание, что вам нужно сделать лямбду mutable, чтобы сделать вызов на rand_ действительным. например,

rand = [=]() mutable {return rand_(mt);};
0 голосов
/ 03 июля 2018

Альтернативой копированию генератора и распространения в лямбде является включение их в класс. Тогда они имеют то же время жизни, что и rand

class montecarlo
{
    std::mt19937 gen;
    std::uniform_real_distribution<double> dis;
  public:
    montecarlo(double x_min, double x_max);
    std::function<double()> rand;
};

montecarlo::montecarlo(double x_min, double x_max)
  : gen(std::random_device()), 
    dis(x_min, x_max),
    rand([this](){ return dis(gen); })
{}

Название этого класса предполагает, что он будет заниматься другими вещами. Я не рекомендую вам расширять его, а вместо этого делить генерацию на собственный класс.

class uniform_real_generator
{
    std::mt19937 gen;
    std::uniform_real_distribution<double> dis;
  public:
    uniform_real_generator(double x_min, double x_max);
    double operator();
}

uniform_real_generator::uniform_real_generator(double x_min, double x_max)
  : gen(std::random_device()), 
    dis(x_min, x_max)
{}

double uniform_real_generator::operator()
{
    return dis(gen);
}

class montecarlo
{
    // other members
public:
    montecarlo(double x_min, double x_max/*, other args */);
    uniform_real_generator rand;
}

montecarlo::montecarlo(double x_min, double x_max/*, other args */)
  : rand(x_min, x_max) //, other member initialisers
{}
0 голосов
/ 03 июля 2018

Это неопределенное поведение, которое должно заканчиваться ошибкой сегментации. Обратите внимание, что ваша лямбда захватывает все по ссылкам, а переменные времени жизни ограничиваются обработчиком конструктора. Поэтому, когда используется лямбда, она работает с переменными, которых больше не существует.

Чтобы исправить это, измените лямбду, чтобы захватывать переменные копией, и это будет работать.

Или сделайте эти переменные полями класса.

...