Переопределение лямбды не разрешено в C++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).

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

5 ответов

Решение

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

#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, Конечно, сообщение об ошибке, которое вы получаете от компилятора, может быть немного яснее в этом отношении...

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

Так что вы делаете следующее:

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

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

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

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

Причина, по которой вы не можете этого сделать, заключается в том, что оператор присваивания копии для лямбда-выражения объявлен удаленным, см. Раздел 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= а не о том, что их типы разные. Хотя каждая лямбда, имеющая свой собственный тип, тоже вам мало поможет.

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

Другие вопросы по тегам