Reputation: 13948
I had the following code:
#include <iostream>
class Foo
{public:
Foo() {}
int a;
};
int main()
{
Foo foo;
auto lambda = [=]() mutable { std::cout << foo.a; };
}
And things were working fine, until I needed to add a copy constructor to my Foo class:
Foo(Foo& t) {}
And it wouldn't compile anymore, giving the message:
class 'Foo': no copy constructor available or copy constructor is declared 'explicit'
I've made the lambda mutable because I didn't want capture a const Foo, but what I think is happening is that the lambda can't be copied. Another compiler had a more useful error message:
error: use of deleted function ‘main()::< lambda()>::< lambda>(main()::< lambda()>&&)’
and:
main()::< lambda()>::< lambda>(main()::< lambda()>&&)’ is implicitly deleted because the default definition would be ill-formed:
But I don't really understand this. Is that implicitly deleted function the move constructor of the lambda? I can't understand why just adding a copy constructor to the captured class (not the lambda) makes this happen.
This is what I picture lambda/functor as looking like:
class lambda
{public:
Foo foo; // <---- My captured variable/class
void operator()(){ std::cout << foo.a; }
}
So then a copy of one of these lambdas to another involves calling Foo's assignment operator or copy constructor? I don't understand how just Foo having a copy constructor makes this fail or what is "ill-formed". The other thing I noticed is that there's no problem when the lambda captures by reference [&].
Edit: It doesn't compile on this compiler:
https://www.jdoodle.com/online-compiler-c++/
I'm on Visual Studio and it wouldn't compile. However when I made a much smaller example it would compiler, but still underline the error. In my larger project it doesn't compile.
Upvotes: 3
Views: 1280
Reputation: 26322
To get a compilation error, you should compile this code under C++11 or C++14 standard. In C++17 it is valid. Demo.
Let's consider
struct Foo {
Foo() {}
Foo(Foo&) {}
};
In C++17 we can write
auto f = Foo{};
But in C++11/14 this line will fail to compile. The reason is that in C++17 we have mandatory copy elision, and the correctness of copy constructor invocation, which would be ill-formed because Foo&
cannot bind to a temporary Foo{}
and Foo(Foo&&)
is deleted, is not even checked by the compiler.
This translates directly into a lambda (rafix07's answer explains how), because it captures Foo
by value. The lambda itself is fine. For example, you can write
[=] { std::cout << foo.a; };
But lambda's move constructor is ill-formed, and in C++11/14 it has to be well-formed for the line
auto lambda = [=] { std::cout << foo.a; };
to compile.
Upvotes: 1
Reputation: 20936
auto lambda = [=]() mutable { std::cout << foo.a; };
on the right you create temporary closure. Based on this temporary closure is constructed another by calling default move constructor generated by compiler.
closure c(closure{});
default implementation of move constructor just moves all data members one by one:
struct closure {
Foo foo;
closure (closure&& theOther) : foo(std::move(theOther.foo)) // <--- [1]
{} // binding rvalue ref to lvalue ref
};
your Foo
ctor takes Foo&
, but it is not allowed to bind rvalue reference to lvalue reference.
It works in MSVC because it has an extension to deal with such thing. Under G++/Clang it must fail.
With const Foo&
, works fine because temporary can be bound to const lvalue ref.
Upvotes: 1
Reputation: 866
The copy constructor prototype is A(const A&)
. You are effectively missing the const
qualifier on your copy constructor which is why the error happens.
Upvotes: 2