• Home   /  
  • Archive by category "1"

Copy Assignment Operator Of Is Implicitly Deleted Because Field Is Of Reference Type


Updated 23rd February 2013: Fixed typos in the code, incorrect use of const, clarified the use of the terms “initialization” and “assignment” to be semantically correct, corrected/clarified references to “constructor” and “assignment operator”, added an explanation of the default copy assignment operator and unique_ptr’s private copy constructor and copy assignment operator.

Updated 12th May 2013: Fixed a factual error regarding standard functions not working as custom deleters in all scenarios, fixed a factual error stating that std::shared_ptr doesn’t support custom deleters (it does), and corrected references to which should have referred to . Expanded the custom deleter section with example cases of new’ing your own resource classes or fetching resources from an external API or factory function. Added text explaining the advantages of using the function object idiom for custom deleters.

The std::unique_ptr class is C++11’s replacement for the flawed std::auto_ptr smart pointer from C++03. std::unique_ptr provides single-ownership pointer semantics and is therefore especially useful when dealing with resource handles which should belong to a single object, and its custom deleter feature (which allows the call to to be replaced with an arbitrary function call when the object is destroyed, such as or ) is great for ensuring resources are closed properly before their handles are freed.

I’m not going to explain how std::unique_ptr or move semantics work here – see the References section at the bottom for some introductory links – rather, I’m going to specifically look at how std::unique_ptr should be used in a non-copyable class (that is, one with a private, or – in C++ 11 – deleted, copy constructor), and specifically, how to make sure the resource is freed exactly and only once.

The Problem

A common design pattern is to declare your resource objects as members of the application class and load resources into them when the application starts. For example, somewhere in your application class you will have a member:

ResourceClass resource;

and in your startup code:

resource = ResourceClass("NameOfResource");

If you’re using std::unique_ptr to store the resource handle in ResourceClass, and it is using the default copy assignment operator, you’ve immediately got a problem on your hands. Consider the class:

class ResourceClass { private: std::unique_ptr<char []> resource; public: ResourceClass() {} ResourceClass(const char *resourceName) { char *data; // do something that loads some binary data into 'data' resource = std::unique_ptr<char []>(data); } };

As long as the instance of isn’t copied, it works correctly: the constructor loads a file or some other block of data and assigns its pointer to . When the object goes out of scope, ‘s destructor is called, which calls on its pointer and frees the memory used by the resource.

If we initialize the object in our application as follows:

ResourceClass resource("NameOfResource");

no copy is made so there is no problem. In all modern C++ compilers, this also works and is considered to be direct initialization, not a copy:

ResourceClass resource = ResourceClass("NameOfResource");

since the copy that would normally be made is elided by the compiler. This is good news for using factory patterns because the following will also work:

ResourceClass LoadSomething(const char *resourceName) { return ResourceClass(resourceName); } ... ResourceClass resource = LoadSomething("NameOfResource");

Here the compiler elides the temporary copy that would normally be made from the return value of .

Unfortunately, C++ does not allow initialization of class members (except for members) at the time of their declaration, so we must set them up by assigning values to them afterwards:

// in application class definition (initializes the object with its default constructor) ResourceClass resource; // in startup code (perhaps the application class constructor) (assigns value) resource = ResourceClass("NameOfResource");

Now, the use of the assignment operator () causes a copy of the temporary object on the right-hand side of the assignment to be made, and attempts to store it in . This invokes ‘s copy assignment operator, which, since we have not defined one explicitly, is the default implementation which simply calls the copy assignment operator for each member in the object. To prevent two instances of std::unique_ptr pointing to the same address, its copy assignment operator is declared private, and is therefore inaccessible to other classes. Therefore, this code (correctly) fails to compile.

The same is also true for std::unique_ptr‘s copy constructor. For example:

std::unique_ptr<int> first(new int); // OK std::unique_ptr<int> second = first; // Fails, copy constructor is private std::unique_ptr<int> third(first); // Fails, copy constructor is private std::unique_ptr<int> fourth; fourth = first; // Fails, assignment operator is private

Move Semantics To The Rescue

Assuming we don’t want to be able to copy the resource but only transfer ownership of which object manages it, the first step is to prevent the object from being copied by explicitly defining a copy constructor and a copy assignment operator, and making them private so that they can’t be called:

class ResourceClass { ... private: // No copying or copy assignment allowed of this class or any derived class ResourceClass(ResourceClass const &); ResourceClass &operator=(ResourceClass const &); ... };

If your compiler supports it (Visual C++ 2010 and 2012 don’t), you may prefer the C++11 delete semantic which specifies a default constructor should not be compiled in:

class ResourceClass { ... public: ResourceClass(ResourceClass const &) = delete; ResourceClass &operator=(ResourceClass const &) = delete; ... };

Now we will define a move assignment operator. The purpose of this is to transfer ownership of the pointer stored in the std::unique_ptr of the object being moved to the new object and is implemented as follows:

class ResourceClass { ... public: ResourceClass &operator=(ResourceClass &&o) { if (this != &o) { resource = std::move(o.resource); } return *this; } };

This directs the compiler to perform a move rather than a copy when is used with a temporary object on the right-hand side. The guards against self-assignment.

Although our particular problem does not call for it, it’s usually a wise idea if you need a constructor or assignment operator to define both (see ‘The Rule of Three‘), so we create a move constructor as well:

class ResourceClass { ... public: ResourceClass(ResourceClass &&o) : resource(std::move(o.resource)) {} ... };

Note that we have not used in the function signatures for the move constructor or move assignment operator. This is because the temporary object must be modifiable so that its std::unique_ptr can have assigned to it.

When a temporary (and only a temporary – ie. one on the right-hand side of an assignment that has been generated by a function or constructor call and not a reference to an already-existing object in another variable) object is assigned from now, the new std::unique_ptr is initialized with the pointer in the std::unique_ptr in the temporary object, but crucially, the pointer in the temporary object is set to (by the new std::unique_ptr‘s move assignment operator invoked as a result of using in our move assignment operator), preventing the memory being pointed to from being freed (‘d) when the temporary object goes out of scope (when the assignment statement completes).

Now let’s go back to our original code:

// in application class definition ResourceClass resource; // in startup code (perhaps the application class constructor) resource = ResourceClass("NameOfResource");

This will now work as desired. The assignment causes ‘s move assignment operator to execute and ownership of the pointer in the std::unique_ptr is transferred correctly. The std::unique_ptr in the temporary object is nulled, the object goes out of scope, and no memory is freed, leaving it intact for use by .

Custom Deleters when std::unique_ptr is a class member

We could have avoided using std::unique_ptr and having to mess around with move semantics altogether above, instead plumping for std::shared_ptr which will allow copies and only the owned pointer when the last copy of std::shared_ptr goes out of scope. Both std::unique_ptr and std::shared_ptr support custom deleters so the correct choice really depends on what kind of ownership semantics you want: use std::unique_ptr when a resource should only be owned by one object, and std::shared_ptr when ownership can be shared among several objects.

Imagine we have a resource type which must be closed by calling its method before being freed. You can handle this scenario as follows:

void closeResource(Resource *res) { res->Close(); } class ResourceClass { private: std::unique_ptr<Resource, void (*)(Resource *)> resource; ... public: ResourceClass(...) : resource(new Resource, closeResource) { // Do something to populate 'resource' if needed } };

The second template parameter to std::unique_ptr specifies the function pointer type for the custom deleter. This must always be a function which takes the raw pointer to the resource as a single argument and returns void.

This is fine if you have written the class yourself or it is some other object that can be instantiated with directly, but sometimes we have to use external libraries, APIs or factory functions which return instances of a resource via a pointer. In these cases, we cannot immediately initialize in ‘s constructor initializer list. However, if we don’t provide an initialization, you will get a compile error such as:

or similar, depending on your compiler (this error is from Visual Studio).

Consider the case where we use an external library factory function to create a resource of type , again defined in the external library. provides a function so we want to use a custom deleter. We can work around the problem like this:

void closeExternalResource(ExternalResource *res) { res->Close(); } class ResourceClass { private: typedef std::unique_ptr<ExternalResource, void (*)(ExternalResource *)> ResourceType; ResourceType resource; ... public: ResourceClass(...) : resource(nullptr, closeExternalResource) { // Create resource ExternalResource *data; CreateResourceFromAPI(&data); resource = ResourceType(data, closeExternalResource); } };

(the is optional but good for readability and reducing typing errors)

As you can see, this requires to be initialized with no pointer in the constructor initializer list, and assigned to afterwards in the constructor itself. This is – in my opinion – a bit messy.

Function object idiom

Instead, we can use the function object idiom. This pattern defines a type with a single which performs the close operation on the resource. It is defined like this:

// Function object to close a resource struct CloseResource { void operator()(Resource *res) const { res->Close(); } };

For the case where we up the resource ourselves, the first example can be re-written as follows:

// The resource wrapper class class ResourceClass { private: std::unique_ptr<Resource, CloseResource> resource; ... public: ResourceClass(...) : resource(new Resource) { // Do something to populate 'resource' if needed } };

The example using the factory function can be re-written like this:

struct CloseExternalResource { void operator()(ExternalResource *res) const { res->Close(); } }; class ResourceClass { private: typedef std::unique_ptr<ExternalResource, CloseExternalResource> ResourceType; ResourceType resource; ... public: ResourceClass(...) { // Create resource ExternalResource *data; CreateResourceFromAPI(&data); resource = ResourceType(data); } };

The difference here between using the function object idiom and a standard function is that we have provided a concrete type as the second template parameter (the function object), and as such, when we initialize the std::unique_ptr we don’t need to specify the custom deleter function as the 2nd regular argument anymore. This saves us from having to initialize the resource in the constructor initializer list when we don’t need to, and from typing the function name every time we place a resource into a std::unique_ptr.

This will now all work as expected: will be called on the object pointed to by std::unique_ptr when the object goes out of scope. If you combine this with the move semantics above, you can return instances of itself from your own factory functions and move them to members of your application class using regular assignment, safe in the knowledge that will not be called when the temporary objects go out of scope – only when the application class does.

Conclusion

There are other ways to solve the problem of correctly destroying a resource exactly once without using std::unique_ptr or move semantics: traditional reference counting, std::shared_ptr, boost::intrusive_ptr (especially useful for COM objects and other objects with an internal reference count), and so on. Each has their pros and cons, and if you really do need copies of the object the techniques outlined here won’t work for you, but in most cases a resource is owned by a single object instance, and C++11 provides an efficient if not entirely neat solution to this in the form of std::unique_ptr and move semantics.

I’m a software developer with very limited work capacity due to having the debilitating illness M.E. – please read my article Dying with M.E. as a Software Developer and donate to the crowdfund to help me with my bucket list if you found this article useful. Thank you so much!

References

CProgramming.com: Move semantics and rvalue references in C++11

InformIT: Using unique_ptr, Part I

InformIT: Using unique_ptr, Part II

SmartBear: The Biggest Changes in C++11 (and Why You Should Care)

CodeProject: Unique_ptr custom deleters and class factories

MSDN: How to: Create and Use unique_ptr Instances

Like this:

LikeLoading...

Related

Categories: C++Tags: C++, C++11, Copy constructor, Custom deleter, Move constructor, Move semantics, Rvalue references, Smart pointer, std::unique_ptr

References are bound to an object when they are initialized and can never be altered after that, everything else you do to them affects the object they are bound to, not the reference itself.

So a reference member is set during construction, and never altered. Since the purpose of an assignment operator is to alter members after construction, it doesn't make sense to generate an implicit assignment operator when one of the member can never be altered. The compiler refuses to try and guess what you want it to do and forces you to provide your own assignment operator with the semantics you want.

Do I need to do anything in my operator= such as explicitly declare return *this or will the compiler (g++) handle this for me?

You absolutely definitely 100% need to

The only time you don't need an explicit return in C++ is if your function returns or in (where there is an implicit if you reach the end of the function) or in unusual cases such as functions that never return (either looping forever or throwing an exception).

Do I have to do anything special with the reference member?

It depends what semantics you expect assignment of your type to have.

If you don't want it to change the object the reference is bound to, fine, do nothing with it.

If you want assignment to alter the object the reference is bound to, you need to do that.

If you want the reference to be re-bound to a different object, you're out of luck, C++ doesn't allow that.

One thought on “Copy Assignment Operator Of Is Implicitly Deleted Because Field Is Of Reference Type

Leave a comment

L'indirizzo email non verrà pubblicato. I campi obbligatori sono contrassegnati *