Document number: N4462

Ville Voutilainen
2015-04-07

LWG 2089, Towards more perfect forwarding

Abstract

It has been known for quite some time that the library object factories (make_unique, make_shared, allocator::construct) don't handle aggregates well, due to the factory functions requiring a parentheses-expression. There seems to be a simple and elegant fix that provides aggregate support without any loss of current functionality; (see the proposed resolution of LWG 2089) making such factories provide both braced-initialization and direct-initialization via a trait check for is_constructible. This paper seeks to solicit EWG feedback on whether the proposed fix is the right solution going forward, or whether EWG thinks there are better alternatives.

Introduction

Currently, allocator::construct is specified as follows:

template <class U, class... Args&>
    void construct(U* p, Args&&... args);
Effects: ::new((void *)p) U(std::forward<Args>(args)...)

This doesn't handle an aggregate that is initializable with U{std::forward<Args>(args)...}. Same problem applies to make_shared and make_unique. LWG 2089 proposes a fix that allows all the current code to work, and allows aggregates to work, too.

The gist of the proposed library fix is simple:

The first bullet will check whether there's a constructor that can construct from the given arguments. An aggregate will not have such a constructor. Non-aggregates will hit the first bullet, aggregates will hit the second bullet.

Simple suggestion

The proposed resolution for LWG 2089

Since it's somewhat unlikely that we will devise some magic-forwarding language facility, it certainly looks like the list above means the proposed resolution of LWG 2089 is an improvement, and we should just encourage LEWG/LWG to go forward with the proposed resolution of LWG 2089, keeping in mind that it needs to be applied to at least all of allocator::construct, allocator_traits::construct, make_unique and make_shared. (uninitialized_copy? uninitialized_fill? Anything else? I can't find any others, EmplaceConstructible is specified in terms of allocator::construct, so emplace cases will Just Work). Note that std::experimental::optional has a similar issue in its in-place constructor, so that should be considered separately, as it's in a different document.

Is that all?

Well, no. One of the reasons this paper is an EWG paper is that while the proposed resolution to LWG 2089 can fix the problem for library factories, and library vendors will certainly have little trouble implementing such a fix, user types don't really have a non-expert solution they can use. Teaching users that they need to eg. (*scary part begins*)delegate to a static member function template of a class template that takes a non-type bool parameter, fully specialize that class template for the false case, and dispatch on the result of is_constructible (or dispatch to tagged overloads, with true_type and false_type)(*scary part ends*) is a fairly significant teachability issue. I don't personally look forward to teaching the bit marked with the *scary* markers - and I don't blame anyone who feels the same.

Some strawman ideas how to provide users with some help: