Подтвердить что ты не робот

Переопределить lambdas не разрешено на С++ 11, почему?

Пример:

#include <functional>

int main() {
  auto test = []{};
  test = []{};

  return 0;
}

В gcc 4.7.2 выдается следующее сообщение об ошибке:

test.cpp: In function ‘int main()’:
test.cpp:5:13: error: no match for ‘operator=’ in ‘test = <lambda closure object>main()::<lambda()>{}’
test.cpp:5:13: note: candidate is:
test.cpp:4:16: note: main()::<lambda()>& main()::<lambda()>::operator=(const main()::<lambda()>&) <deleted>
test.cpp:4:16: note:   no known conversion for argument 1 from ‘main()::<lambda()>’ to ‘const main()::<lambda()>&’

Из стандарта 5.1.2.3 (акцент мой):

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

- размер и/или выравнивание типа закрытия,

- существует ли тип закрытия тривиально скопируемый (раздел 9)

- является ли тип закрытия стандартным классом макета (раздел 9) или

- является ли тип замыкания классом POD (раздел 9).

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

4b9b3361

Ответ 1

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

#include <functional>
#include <type_traits>
#include <iostream>

int main() {
  auto test = []{};
  auto test2 = []{};
  std::cout << std::is_same< decltype( test ), decltype( test2 ) >::value << std::endl;
  return 0;
}

напечатает 0. Конечно, сообщение об ошибке, которое вы получаете от компилятора, может быть немного понятнее в этом отношении...

Ответ 2

Тип лямбда-выражение (который также является типом объекта замыкания) представляет собой уникальный, неназванный, тип класса объединения

Итак, это похоже на то, что вы делаете следующее:

struct {} a;
struct {} b;
a = b; // error, type mismatch

Используйте std::function, если вы хотите назначить разные lambdas с одной и той же сигнатурой той же переменной.

std::function<void()> f = []{};
f = []{}; //ok

Ответ 3

Lambda нельзя переопределить, потому что каждая лямбда имеет другой, анонимный, несовместимый тип. Они могут быть скопированы только в том случае, если вы передадите их в шаблонную функцию (например, std::function ctor), которая сможет вывести этот тип.

Ответ 4

Если бы мы могли назначить одну лямбду другой лямбда другого типа, как мы можем скопировать тела/определения функции из этой лямбда в другую? Если бы мы были настолько упрямы, тогда мы могли бы использовать некоторый тип типа std::function, который будет скопирован. Но это было бы против правила ol 'С++ о том, чтобы не платить бла-бла...

Ответ 5

Причина, по которой вы не можете этого сделать, заключается в том, что оператор присваивания копий для лямбда-выражения объявлен удаленным, см. раздел 5.1.2/20 стандарта. Для более четкого (для необычных определений ясного) см. Этот пример кода

template<class T> void f(T x1)
{
  T x2 = x1; // copy constructor exists, this operation will succeed.
  x2 = x1; // assignment operator, deleted and will cause an error
}
int main()
{
  f([]{});
  return 0;
}

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