make_shared with copy elision

245 views
Skip to first unread message

col...@yahoo.co.uk

unread,
Jul 7, 2016, 7:11:03 AM7/7/16
to ISO C++ Standard - Future Proposals
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?

Nicola Gigante

unread,
Jul 7, 2016, 7:42:38 AM7/7/16
to std-pr...@isocpp.org

> Il giorno 07 lug 2016, alle ore 13:11, col3435 via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> ha scritto:
>
> 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() { /* ... */ }
>

Why should copy elision change which types you can return from the function?
If the struct is non-moveable and non-copyable you cannot move nor copy its objects,
whether copy elision is performed or not.

Nicola

Ville Voutilainen

unread,
Jul 7, 2016, 8:13:52 AM7/7/16
to ISO C++ Standard - Future Proposals
On 7 July 2016 at 14:42, Nicola Gigante <nicola....@gmail.com> wrote:
>
>> Il giorno 07 lug 2016, alle ore 13:11, col3435 via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> ha scritto:
>>
>> 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() { /* ... */ }
>>
>
> Why should copy elision change which types you can return from the function?

Because when elision becomes mandatory, rather than a possible
optimization, nonmoveable types can be
returned.

> If the struct is non-moveable and non-copyable you cannot move nor copy its objects,
> whether copy elision is performed or not.


With http://open-std.org/JTC1/SC22/WG21/docs/papers/2015/p0135r0.html, you can.

Nicol Bolas

unread,
Jul 7, 2016, 9:43:10 AM7/7/16
to ISO C++ Standard - Future Proposals, col...@yahoo.co.uk

It's an interesting idea in the wake of guaranteed elision. But I think we need a bit more user-experience with the idiom before we can start standardizing it.

Matt Calabrese

unread,
Jul 7, 2016, 2:24:37 PM7/7/16
to ISO C++ Standard - Future Proposals
On Thu, Jul 7, 2016 at 4:11 AM, col3435 via ISO C++ Standard - Future Proposals <std-pr...@isocpp.org> wrote:
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 is akin to a solution that I've used over the years in order to minimize copies/moves and it works very well. Going further, IMO, in an ideal world any facility in the standard library that does emplacement should also be able to emplace with the result of a function, but we sort of missed an easy route forward for that in the standard due to how we specify emplacement and in-place constructors -- I think that if we wanted to have a really robust and user-extensible way to do optimal emplacement in standard-library facilities without introducing additional overloads, we should have adopted something more similar to the boost in_place_factory facilities[1] for doing emplacement, only dealing at a more abstract and formally specified concept-level. Each factory would have an associated "create" function in addition to or in place of boost's "apply" associated function. The "create" function would return the constructed object, which is perfect for use with in-place construction of datamembers from a constructor in a way that removes the need for copy/move. If we had a concept like that in the standard and emplacement/in_place-construction dealt with that concept, it would be trivial to have user-extensible emplacement that works in a non-intrusive manner with existing containers, such as a factory that emplaces with a function's result, and it would isolate handling of std::initializer_list. It also would make it possible to do things like construct and store an immovable object in a variant, which is a limitation I'm running into at the moment.

Marc

unread,
Jul 8, 2016, 11:03:52 AM7/8/16
to ISO C++ Standard - Future Proposals, cala...@x.team

Note that if you replace the function 'create' with a conversion operator, you get an implicit lazy function call, which is more likely to work with existing code.
Reply all
Reply to author
Forward
0 new messages