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.
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:
is_constructible_v<TargetType, Args...>
, use direct-nonlist-initializationThe 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.
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.
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:
is_constructible
- in other words,
expose parts of the implementation technique that library vendors are
likely going to use, as a simple library function.