This page is a snapshot from the LWG issues list, see theLibrary Active Issues List for more information and the meaning ofResolved status.
std::allocator::construct should use uniform initializationSection: 20.2.10.2[allocator.members]Status:ResolvedSubmitter: David KraussOpened: 2011-10-07Last modified: 2020-09-06
Priority:2
View all otherissues in [allocator.members].
View all issues withResolved status.
Discussion:
When theEmplaceConstructible (23.2.2[container.requirements.general]/13) requirement is used to initialize an object, direct-initialization occurs. Initializing an aggregate or using astd::initializer_list constructor with emplace requires naming the initialized type and moving a temporary. This is a result ofstd::allocator::construct using direct-initialization, not list-initialization (sometimes called "uniform initialization") syntax.
std::allocator<T>::construct to use list-initialization would, among other things, give preference tostd::initializer_list constructor overloads, breaking valid code in an unintuitive and unfixable way — there would be no way foremplace_back to access a constructor preempted bystd::initializer_list without essentially reimplementingpush_back.std::vector<std::vector<int>> v;v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initializationThe proposed compromise is to use SFINAE withstd::is_constructible, which tests whether direct-initialization is well formed. Ifis_constructible is false, then an alternativestd::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.
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 thestd::initializer_list type, as inv.emplace_back(std::initializer_list<int>(3, 4)). Since this matches the semantics as ifstd::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 becauseEmplaceConstructible is defined in terms of whatever allocator is specified, and there is no need to explicitly mention SFINAE in the normative text.[2012, Kona]
Move to Open.
There appears to be a real concern with initializing aggregates, that can be performed onlyusing brace-initialization. There is little interest in the rest of the issue, given the existenceof 'emplace' methods in C++11.
Move to Open, to find an acceptable solution for intializing aggregates. There is the potentialthat EWG may have an interest in this area of language consistency as well.
[2013-10-13, Ville]
This issue is related to2070(i).
[2015-02 Cologne]
Move to EWG, Ville to write a paper.
[2015-09, Telecon]
Ville:N4462 reviewed in Lenexa. EWG discussion to continue in Kona.
[2016-08 Chicago]
SeeN4462
The notes in Lenexa say that Marshall & Jonathan volunteered to write a paper on this
[2018-08-23 Batavia Issues processing]
P0960 (currently in flight) should resolve this.
[2020-01 Resolved by the adoption ofP0960 in Kona.]
Proposed resolution:
This wording is relative to the FDIS.
Change 20.2.10.2[allocator.members] p12 as indicated:
template <class U, class... Args> void construct(U* p, Args&&... args);12Effects:
::new((void *)p) U(std::forward<Args>(args)...)ifis_constructible<U, Args...>::valueistrue, else::new((void *)p) U{std::forward<Args>(args)...}