What is the best smart pointer return type for a factory function?

I’ll answer in reverse order so to begin with the simple cases.

Utility functions that accept objects from the factory functions, use them, but do not take ownership. (For example a function that counts the number of words in the document.)

If you are calling a factory function, you are always taking ownership of the created object by the very definition of a factory function. I think what you mean is that some other client first obtains an object from the factory and then wishes to pass it to the utility function that does not take ownership itself.

In this case, the utility function should not care at all how ownership of the object it operates on is managed. It should simply accept a (probably const) reference or – if “no object” is a valid condition – a non-owning raw pointer. This will minimize the coupling between your interfaces and make the utility function most flexible.

Functions that keep a reference to the object after they return (like a UI component that takes a copy of the object so it can draw the content on the screen as needed.)

These should take a std::shared_ptr by value. This makes it clear from the function’s signature that they take shared ownership of the argument.

Sometimes, it can also be meaningful to have a function that takes unique ownership of its argument (constructors come to mind). Those should take a std::unique_ptr by value (or by rvalue reference) which will also make the semantics clear from the signature.

A factory function (outside of the class) that creates objects and returns them to users of the class. (For example opening a document and returning an object that can be used to access the content.)

This is the difficult one as there are good arguments for both, std::unique_ptr and std::shared_ptr. The only thing clear is that returning an owning raw pointer is no good.

Returning a std::unique_ptr is lightweight (no overhead compared to returning a raw pointer) and conveys the correct semantics of a factory function. Whoever called the function obtains exclusive ownership over the fabricated object. If needed, the client can construct a std::shared_ptr out of a std::unique_ptr at the cost of a dynamic memory allocation.

On the other hand, if the client is going to need a std::shared_ptr anyway, it would be more efficient to have the factory use std::make_shared to avoid the additional dynamic memory allocation. Also, there are situations where you simply must use a std::shared_ptr for example, if the destructor of the managed object is non-virtual and the smart pointer is to be converted to a smart pointer to a base class. But a std::shared_ptr has more overhead than a std::unique_ptr so if the latter is sufficient, we would rather avoid that if possible.

So in conclusion, I’d come up with the following guideline:

  • If you need a custom deleter, return a std::shared_ptr.
  • Else, if you think that most of your clients are going to need a std::shared_ptr anyway, utilize the optimization potential of std::make_shared.
  • Else, return a std::unique_ptr.

Of course, you could avoid the problem by providing two factory functions, one that returns a std::unique_ptr and one that returns a std::shared_ptr so each client can use what best fits its needs. If you need this frequently, I guess you can abstract most of the redundancy away with some clever template meta-programming.

Leave a Comment

Hata!: SQLSTATE[HY000] [1045] Access denied for user 'divattrend_liink'@'localhost' (using password: YES)