Title
std::allocator::construct should use uniform initialization
Status
resolved
Section
[allocator.members]
Submitter
David Krauss

Created on 2011-10-07.00:00:00 last changed 59 months ago

Messages

Date: 2020-01-13.07:01:29

Proposed resolution:

This wording is relative to the FDIS.

Change [allocator.members] p12 as indicated:

template <class U, class... Args>
  void construct(U* p, Args&&... args);

12 Effects: ::new((void *)p) U(std::forward<Args>(args)...) if is_constructible<U, Args...>::value is true, else ::new((void *)p) U{std::forward<Args>(args)...}

Date: 2020-01-13.07:01:29

[ 2020-01 Resolved by the adoption of P0960 in Kona. ]

Date: 2018-08-23.00:00:00

[ 2018-08-23 Batavia Issues processing ]

P0960 (currently in flight) should resolve this.

Date: 2016-08-02.17:19:11

[ 2016-08 Chicago ]

See N4462

The notes in Lenexa say that Marshall & Jonathan volunteered to write a paper on this

Date: 2017-02-02.00:41:18

[ 2015-09, Telecon ]

Ville: N4462 reviewed in Lenexa. EWG discussion to continue in Kona.

Date: 2015-03-29.16:39:30

[ 2015-02 Cologne ]

Move to EWG, Ville to write a paper.

Date: 2013-10-15.00:00:00

[ 2013-10-13, Ville ]

This issue is related to 2070.

Date: 2012-02-27.16:24:02

[ 2012, Kona ]

Move to Open.

There appears to be a real concern with initializing aggregates, that can be performed only using brace-initialization. There is little interest in the rest of the issue, given the existence of 'emplace' methods in C++11.

Move to Open, to find an acceptable solution for intializing aggregates. There is the potential that EWG may have an interest in this area of language consistency as well.

Date: 2011-12-17.13:10:28

When the EmplaceConstructible ([container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using a std::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result of std::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.

Altering std::allocator<T>::construct to use list-initialization would, among other things, give preference to std::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way for emplace_back to access a constructor preempted by std::initializer_list without essentially reimplementing push_back.

std::vector<std::vector<int>> v;
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization

The proposed compromise is to use SFINAE with std::is_constructible, which tests whether direct-initialization is well formed. If is_constructible is false, then an alternative std::allocator::construct overload is chosen which uses list-initialization. Since list-initialization always falls back on direct-initialization, the user will see diagnostic messages as if list-initialization (uniform-initialization) were always being used, because the direct-initialization overload cannot fail.

I can see two corner cases that expose gaps in this scheme. One occurs when arguments intended for std::initializer_list satisfy a constructor, such as trying to emplace-insert a value of {3, 4} in the above example. The workaround is to explicitly specify the std::initializer_list type, as in v.emplace_back(std::initializer_list<int>(3, 4)). Since this matches the semantics as if std::initializer_list were deduced, there seems to be no real problem here.

The other case is when arguments intended for aggregate initialization satisfy a constructor. Since aggregates cannot have user-defined constructors, this requires that the first nonstatic data member of the aggregate be implicitly convertible from the aggregate type, and that the initializer list have one element. The workaround is to supply an initializer for the second member. It remains impossible to in-place construct an aggregate with only one nonstatic data member by conversion from a type convertible to the aggregate's own type. This seems like an acceptably small hole.

The change is quite small because EmplaceConstructible is defined in terms of whatever allocator is specified, and there is no need to explicitly mention SFINAE in the normative text.

History
Date User Action Args
2020-01-13 07:01:29adminsetmessages: + msg10922
2020-01-13 07:01:29adminsetstatus: ewg -> resolved
2018-08-24 13:31:33adminsetmessages: + msg10108
2016-08-02 17:19:11adminsetmessages: + msg8322
2015-09-27 21:21:40adminsetmessages: + msg7559
2015-03-29 16:39:30adminsetmessages: + msg7264
2015-03-29 16:39:30adminsetstatus: open -> ewg
2014-01-12 12:16:41adminsetmessages: + msg6772
2012-02-27 16:24:02adminsetmessages: + msg6023
2012-02-12 18:36:43adminsetstatus: new -> open
2011-11-26 14:57:44adminsetmessages: + msg5910
2011-10-07 00:00:00admincreate