It seems that C++17 will allow us to write "named constructors" even for non-moveable types due to guaranteed copy elision, e.g.
struct NonMoveable { /* ... */ };
NonMoveable makeNonMoveable() { /* ... */ }
However, named constructors like this one are still very much second-class citizens when it comes to the standard library. For example, no amount of copy elision will ever allow us to do this:
std::make_shared<NonMoveable>(makeNonMoveable())
And even if we had a moveable type, calling make_shared with the result of a named constructor would still incur the cost of a move construction, which really shouldn't be necessary.
Is there any way to work around this limitation? Perhaps we could do something like:
struct Helper
{
operator NonMoveable() { return makeNonMoveable(); }
};
std::make_shared<NonMoveable>(Helper{});
But this seems arcane, and wouldn't necessarily work as a generic solution (maybe I'm missing something but I think this must break down if NonMoveable had a poorly-constrained templated constructor).
In the absence of any better workarounds, perhaps we need an alternative to make_shared, let's call it make_shared_invoke, which takes a callable as its first argument and invokes that to create the value:
make_shared_invoke(makeNonMoveable)
This would have additional advantages:
(i) It automatically deduces the type of the shared_ptr.
(ii) It's more general than make_shared. For example, it provides an obvious way to control whether () or {} initialization is used: just use make_shared_invoke with a lambda.
Of course, it's not just make_shared. Any other library function which forwards its arguments to a constructor might have the same issue. How about:
std::deque<NonMoveable> d;
d.emplace_back_invoke(makeNonMoveable);
Any thoughts on this approach, or better alternatives available?