How to return an object without copy constructor

My questions are about how to return an object that does not have a copy constructor. As an example, suppose I have a bigResource that sits on a heap and say that I track it with unique_ptr . Now suppose I transfer this resource to the caterpillar owner. Then I have CaterpillarWithBigResource . Now at some point this CaterpillarWithBigResource will turn into a ButterflyWithBigResource , so the Caterpillar object will have to transfer ownership of the Butterfly object.

I wrote the following code to simulate a situation:

 #include <cstdlib> #include <iostream> #include <memory> class ButterflyWithBigResource { public: // If I uncomment just this line, I get an error // ButterflyWithBigResource(const ButterflyWithBigResource& other) = default; // If I uncomment just this line, I get an error // ButterflyWithBigResource(const ButterflyWithBigResource& other) = delete; // With both above lines commented out, I get no errors, and the program runs fine. ButterflyWithBigResource(std::unique_ptr<int>&& bigResource) : bigResource(std::move(bigResource)) { } const int& getResource() { return *bigResource; } private: std::unique_ptr<int> bigResource; }; class CaterpillarWithBigResource { public: CaterpillarWithBigResource(int bigResource) : bigResource(new int(bigResource)) { } ButterflyWithBigResource toButterfly() && { return ButterflyWithBigResource(std::move(bigResource)); } private: std::unique_ptr<int> bigResource; }; /* * */ int main(int argc, char** argv) { CaterpillarWithBigResource caterpillarWithBigResource(5); ButterflyWithBigResource butterflyWithBigResource(std::move(caterpillarWithBigResource).toButterfly()); std::cout << butterflyWithBigResource.getResource() << std::endl; return 0; } 

Note that neither Caterpillar nor Butterfly have default copy constructors, as each one has unique_ptr . However, I would not expect this to be a problem, so you only need to move the constructors. In the end, I transfer ownership only from Caterpillar to Butterfly .

In fact, when I compile the program using g++ -c -g -std=c++11 -MMD -MP -MF using g++ version 4.8.2 , it works fine.

But now the strangest thing is if I remind the compiler that the Butterfly copy constructor is removed by adding the string ButterflyWithBigResource(const ButterflyWithBigResource& other) = delete; , the program no longer compiles, and the compiler complains that the copy constructor has been removed, so I cannot return Butterfly in the toButterfly method.

If I try to say that everything is fine, instead of the string ButterflyWithBigResource(const ButterflyWithBigResource& other) = default; I get the same error again.

I want the Butterfly built in the toButterfly method to be moved to the toButterfly return address, and then later used as an argument to the Butterfly move constructor when building ButterflyWithBigResource in main() . Is there any way to do this?

+7
c ++ copy-constructor c ++ 11 move-semantics move-constructor
source share
1 answer

When you comment out both lines, which are explicitly the default and delete copy constructor, the compiler can implicitly generate a move constructor (and a move operator) for you.

By explicitly defaulting or delete with the copy constructor, you suppress the implicit generation of the move constructor.

From N3337, ยง12.8 / 9 [class.copy]

If the definition of class X does not explicitly declare the move constructor, it will be declared as implicit as default, if and only if
- X does not have a user-declared copy constructor,
- ...

If the move constructor is no longer generated, the return value from toButterfly() should be copied, but this does not work regardless of whether you have deleted or deleted the copy constructor by default.

In the case when you are the default copy constructor, the compiler cannot generate a default constructor implementation due to the presence of the unique_ptr data unique_ptr (which is not copied).

When you delete the copy constructor, if it is selected using overload resolution, this is an error.


You do not need to explicitly delete the copy constructor, since, as explained above, the presence of the unique_ptr data unique_ptr implicitly removes it, but if you want to do this, then you also need to explicitly specify the default value of the move constructor (and the move assignment operator too, if you want so that the move destination works)

 ButterflyWithBigResource(ButterflyWithBigResource&&) = default; ButterflyWithBigResource& operator=(ButterflyWithBigResource&&) = default; 

Live demo

+7
source share

All Articles