Нет подходящего конструктора копирования после захвата класса с помощью лямбды - PullRequest
3 голосов
/ 23 апреля 2020

У меня был следующий код:

#include <iostream>

class Foo
{public:
    Foo() {}
    int a;
};

int main()
{
    Foo foo;

    auto lambda = [=]() mutable { std::cout << foo.a; };

}

И все работало нормально, пока мне не нужно было добавить конструктор копирования в мой класс Foo:

Foo(Foo& t) {}

И это не будет больше не компилируется, выдавая сообщение:

класс 'Foo': конструктор копирования недоступен или конструктор копирования объявлен 'явным'

Я сделал лямбду мутабельной потому что я не хотел захватывать const Foo, но я думаю, что происходит то, что лямбда не может быть скопирована. У другого компилятора было более полезное сообщение об ошибке:

ошибка: использование удаленной функции 'main () :: :: (main () :: &&) '

и:

main () :: :: (main () :: &&) 'неявно удаляется, потому что определение по умолчанию будет неверно сформировано:

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

Вот как я представляю лямбду / функтор следующим образом:

class lambda
{public:

     Foo foo; // <---- My captured variable/class
     void operator()(){ std::cout << foo.a; }
}

Итак, когда копирование одной из этих лямбд в другую включает вызов оператора присваивания Foo или конструктора копирования? Я не понимаю, как просто Foo, имеющий конструктор копирования, делает этот сбой или что "плохо сформировано". Еще одна вещь, которую я заметил, состоит в том, что нет проблем, когда лямбда захватывает по ссылке [&].

Редактировать: Он не компилируется на этом компиляторе:

https://www.jdoodle.com/online-compiler-c++/

Я в Visual Studio, и он не будет компилироваться. Однако, когда я сделал гораздо меньший пример, он бы компилировал, но все же подчеркнул ошибку. В моем более крупном проекте он не компилируется.

Ответы [ 3 ]

2 голосов
/ 23 апреля 2020

Конструктор копирования * прототип - A(const A&). Вы фактически пропускаете квалификатор const в конструкторе копирования, поэтому возникает ошибка.

1 голос
/ 23 апреля 2020

Чтобы получить ошибку компиляции, вы должны скомпилировать этот код в соответствии со стандартами C ++ 11 или C ++ 14. В C ++ 17 это действительно. Демо .

Давайте рассмотрим

struct Foo {
    Foo() {}
    Foo(Foo&) {}
};

В C ++ 17 мы можем написать

auto f = Foo{};

Но в C ++ 11/14 эта строка не скомпилируется. Причина в том, что в C ++ 17 у нас есть обязательное копирование elision и правильность вызова конструктора копирования, что было бы некорректно, потому что Foo& не может связываться с временными Foo{} и Foo(Foo&&) удаляется, даже не проверяется компилятором.

Это переводит прямо в лямбду (ответ rafix07 объясняет как), потому что она захватывает Foo по значению. Сама лямбда в порядке. Например, вы можете написать

[=] { std::cout << foo.a; };

Но конструктор перемещения лямбды плохо сформирован, и в C ++ 11/14 он должен быть правильно сформирован для строки

auto lambda = [=] { std::cout << foo.a; };

скомпилировать.

1 голос
/ 23 апреля 2020
auto lambda = [=]() mutable { std::cout << foo.a; };

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

closure c(closure{});

реализация по умолчанию конструктора move просто перемещает все элементы данных по одному:

struct closure {
    Foo foo;

    closure (closure&& theOther) : foo(std::move(theOther.foo))   // <--- [1]
    {}                              // binding rvalue ref to lvalue ref
};

ваш Foo ctor занимает Foo&, но ему не разрешено связывать rvalue ссылку на lvalue ссылку.

Он работает в MSV C, потому что у него есть расширение для работы с такими вещами. Под G ++ / Clang он должен завершиться с ошибкой.

С const Foo& работает нормально, потому что временный может быть связан с const lvalue ref.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...