Что происходит, когда присваивается тип, содержащий лямбду?

Скажем, у меня есть какой-то тип, обертывающий функцию, может быть, лямбда-функция:

template<typename Function>
  struct my_struct
{
  Function f;

  my_struct(const Function &f) : f(f) {}
};

Что происходит, когда назначается экземпляр этого типа? Насколько я понимаю, лямбда-выражения неизменяемы и имеют удаленные операторы присваивания.

Тем не менее, когда я присваиваю объекту этот тип в приведенном ниже фрагменте кода, ошибка не возникает:

// a structure which contains a function;
// possibly a lambda function
template<typename Function>
  struct my_struct
{
  Function f;

  my_struct(const Function &f) : f(f) {}

  // XXX adding this assignment operator causes an error
  //my_struct &operator=(const my_struct &other)
  //{
  //  f = other.f;
  //  return *this;
  //}
};

template<typename Function>
my_struct<Function> make_struct(const Function &f)
{
  return my_struct<Function>(f);
}

int main()
{
  // create some lambda
  auto lambda = [](int x){return x;};

  // make a struct containing a copy of the lambda
  auto x = make_struct(lambda);

  // try to assign to the struct, which
  // presumably assigns to the enclosed lambda
  x = make_struct(lambda);

  return 0;
}

Добавление закомментированного оператора присваивания приводит к ошибке, как и ожидалось:

$ g++-4.6 -std=c++0x test.cpp
test.cpp: In member function ‘my_struct<Function>& my_struct<Function>::operator=(const my_struct<Function>&) [with Function = main()::<lambda(int)>, my_struct<Function> = my_struct<main()::<lambda(int)> >]’:
test.cpp:34:25:   instantiated from here
test.cpp:13:5: error: use of deleted function ‘main()::<lambda(int)>& main()::<lambda(int)>::operator=(const main()::<lambda(int)>&)’
test.cpp:27:18: error: a lambda closure type has a deleted copy assignment operator

Итак, можно ли создавать назначаемые типы с переменными-членами лямбда? Это кажется разумным желанием попробовать. Например, рассмотрите возможность объединения лямбды с boost::transform_iterator.


person Jared Hoberock    schedule 12.12.2011    source источник
comment
Я еще мало что знаю о перемещении, но возможно ли, что их можно перемещать, но не копировать? Возможно, вы уже знаете ответ на этот вопрос, но я не разбираюсь в движущихся персонажах, поэтому, если знаете, расскажите.   -  person Seth Carnegie    schedule 13.12.2011
comment
Спасибо за идею, но введение оператора перемещения, похоже, не изменило сообщение об ошибке.   -  person Jared Hoberock    schedule 13.12.2011


Ответы (1)


Вы близко. Лямбда имеет неявный конструктор копирования и может иметь — в зависимости от захваченных значений — неявный конструктор перемещения. У него удален оператор копирования-присваивания.

Другими словами, вы можете создать его, но не можете назначить. Если вы ищете общий объект функции, вы хотите использовать std::function<>. Он эмулирует функции как значения первого класса.


Обратите внимание, что неизменяемый отличается от назначаемого. Когда лямбда называется изменяемой, это означает, что ее тело вызова функции может изменять члены лямбды (т. е. функция не является const):

int i = 0;

auto okay = [=](int x) mutable { i += x; };
auto error = [=](int x) { i += x; };

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

person GManNickG    schedule 12.12.2011
comment
Спасибо за объяснение. Жаль, что присваивание удалено — кажется, что это делает лямбду несовместимой с boost::transform_iterator, по крайней мере, с его текущей реализацией. - person Jared Hoberock; 13.12.2011
comment
См. этот ответ для других обходных путей (используя std::ref, предлагаемую библиотеку Boost Fit и предлагаемую библиотеку расширений диапазона Boost). ). - person Frederik Aalund; 11.03.2016
comment
но как тогда std::function может сделать копию лямбды? - person Chris Becke; 11.05.2016