Reputation: 314
I have this simple program that doesn't compile
#include <future>
class Foo
{
public:
~Foo();
std::future<int> f;
};
Foo getFoo()
{
return Foo();
}
int main()
{
Foo b = getFoo();
}
which, I guess, makes sense. Foo
is not copyable because future
is not copyable.
In function 'Foo getFoo()':
13:16: error: use of deleted function 'Foo::Foo(const Foo&)'
4:7: note: 'Foo::Foo(const Foo&)' is implicitly deleted because the default definition would be ill-formed:
4:7: error: use of deleted function 'std::future<_Res>::future(const std::future<_Res>&) [with _Res = int]'
In file included from 2:0:
/usr/include/c++/4.9/future:686:7: note: declared here
future(const future&) = delete;
^
In function 'int main()':
18:20: error: use of deleted function 'Foo::Foo(const Foo&)'
What I don't understand is why it compiles when I remove the destructor:
#include <future>
class Foo
{
public:
//~Foo();
std::future<int> f;
};
Foo getFoo()
{
return Foo();
}
int main()
{
Foo b = getFoo();
}
Isn't this destructor generated by default anyway? What effect does it have on the class being copyable/movable?
Upvotes: 1
Views: 85
Reputation: 22152
The implicitly generated copy constructor is defined as deleted because your class contains a std::future
, which is not copyable. This means your class is not copyable in either case.
It may however be movable. Usually the example you show could also use a move constructor, but the implicit move constructor is not declared when you declare a destructor manually, making Foo
in the first example also not movable, while it is in the second one.
As mentioned by @Ayxan in a comment under the question, since C++17 both your examples would compile, because although Foo
isn't movable in the first example, since C++17 mandatory copy elision rules apply and there would only be one Foo
constructed directly into Foo b
in your example. No temporary Foo
objects would exist and the class would not need to be copyable or movable for this.
In general you should always implement copy/move constructor and assignment operators manually if you have a custom destructor. That is known as the rule of 3/5. The language does not currently enforce this rule for the copy operations, although the implicit declaration of the copy operations if a user-declared destructor exists has been deprecated since C++11. The language does however enforce the rule for the move operations by not implicitly declaring the move operations if a user-declared destructor is present.
A custom destructor is not often needed, so you should probably not declare it in the first place. If you need to declare it, because e.g. you need it to be virtual
(in which case it should just be defaulted with virtual ~Foo() = default
), then you can default the move operations:
Foo(Foo&&) = default;
Foo& operator=(Foo&&) = default;
But if your destructor has a custom definition that does have statements in it that actually perform some work, you likely must implement these methods yourself with proper semantics, so that you don't run into problems when the class is moved.
Upvotes: 3
Reputation: 7100
In c++11, The implicit definition of a copy constructor as defaulted is deprecated if the class has a user-declared copy assignment operator or a user-declared destructor.
It's the standard of the language.
Note that you have a noncopyable member too.
Upvotes: 1