Using std::unique_ptr and std::move
Programming / detail review


Fan(Fan *peer__ = NULL);

  
  (and some more occurrences)


Identifiers that contain a double underscore are reserved for the compiler/libary ("C++ implementation").

The NULL macro can be thought of as deprecated. Use the nullptr keyword instead. It is more typesafe.


static Fan * create(Fan *peer = NULL);



Since those pointers you return from create own a resource (dynamically allocated memory / an object with dynamic storage duration), you should consider making them smart pointers:

static std::unique_ptr&lt;Fan&gt; create(Fan *peer = nullptr);


The function parameter can remain a raw pointer, since it does not convey/transfer ownership. One could argue that some dumb pointer wrapper that explicitly states it does not own a resource might be more appropriate.


Inproc(std::unique_ptr&lt;Fan&gt; bound__,
       std::unique_ptr&lt;Fan&gt; connected__);



In your original version, you used a std::unique_ptr&lt;Fan&gt;&amp;&amp;. Let's examine the alternatives:

void myfunc(std::unique_ptr&lt;Fan&gt;);
void myfunc(std::unique_ptr&lt;Fan&gt;&amp;&amp;);
void myfunc(std::unique_ptr&lt;Fan&gt;&amp;);
void myfunc(std::unique_ptr&lt;Fan&gt; const&amp;);


Only the first guarantees that the unique_ptr you pass in will be empty afterwards. All others do not, by means of (parameter) types, tell you what happens to your pointer. For more details, see the solution of GotW #105: Smart Pointers, Part 3

If you want to always acquire ownership of the pointer argument, you should define your interface using pass-by-value for unique_ptrs.

Let's see how we can refactor Inproc::create() using these changes:

unique_ptr&lt;Inproc&gt; Inproc::create() {
    if (auto bound = Fan::create() ) {
        if (auto connected = Fan::create(bound.get())) {
            return new(std::nothrow) Inproc(std::move(bound), std::move(connected));
        }
    }
    return nullptr;
}


Note that by returning nullptr in all cases of an error, you lose information about what error occurred.



Design review


private:
    Fan(Fan *peer__ = NULL);



Derived classes can only copy a Fan, they cannot create a new one. This seems contradictory to the virtual dtor, which seems to provide "support" for inheritance.


static Fan * create(Fan *peer = NULL);



From the code shown, it is unclear to me why you need to restrict Fans to be created via this factory function (is there a pattern name for this?). It certainly decreases reusability of this class.

For Inproc::create(), the situation could even be worse: You cannot test Inproc independently of Fan, since you can't provide some kind of mocked Fan to construct it. Also, it doesn't seem to require the dynamic storage duration of its unique_ptr subobjects; but again, we don't see the whole program here.

nwp's answer reminded me that you might use pointers to indicate an error. I think that is not a good reason to require dynamic storage duration (-> new + pointers). One solution could be a factory function that returns an optional, or that creates objects in-place at a location passed as a parameter. The former can even be used easily for aggregation; neither can easily be used for inheritance. Another option is to introduce an empty / error state, that the object is set to if construction fails. I don't really like the idea of "empty states" for a class, since those weaken the guarantees / invariants it can provide. But it's a common sight to be able to implement e.g. default constructors. Look at std::thread, std::Xstream etc. - they all have default constructors, even though they leave the object in an almost unusable state.