I noticed that C++23 added new overloads for the std::stack
and std::queue
container adapters' constructors, which allow to construct the underlying container with the contents of the range [first, last)
. cppreference also shows how these overloads can be used with std::initializer_list
, and provides the following examples:
const auto il = {2, 7, 1, 8, 2};
std::stack<int> c4 { il.begin(), il.end() }; // overloads (6), C++23
const auto il = {2, 7, 1, 8, 2};
std::queue<int> c4 { il.begin(), il.end() }; // overload (6), C++23
This means that introducing other overloaded constructors to construct the underlying container with the contents of an std::initialization_list
is not necessary. However, C++23 had added also other container adapters, such as std::flat_set
, and cppreference shows an example of implementation of the std::flat_set
container adapter, where the following overloaded constructors are provided:
flat_set(initializer_list<key_type> il, const key_compare& comp = key_compare())
: flat_set(il.begin(), il.end(), comp) { }
template<class Allocator>
flat_set(initializer_list<key_type> il, const key_compare& comp, const Allocator& a);
template<class Allocator>
flat_set(initializer_list<key_type> il, const Allocator& a);
Why do std::flat_set
and std::flat_map
have overloaded constructors for std::initializer_list
while other container adapters don't?
I'll copy my std-proposals answer here, since it covers a bit more than LoS's own answer above.
First, a quotation from P2447R3:
Yes, any change to overload sets (particularly the addition of new non-explicit constructors) can break code. But that's not necessarily a proposal-killer. For example, there was nothing wrong with C++23’s adopting [P1425] "Iterator-pair constructors for stack and queue" with no change to Annex C, despite its breaking code like this:
void zero(queue<int>); void zero(pair<int*,int*>); int a[10]; void test() { zero({a, a+10}); }
Before: Calls
zero(pair<int, int>)
.
After P1425: Ambiguous.
To fix: Eliminate the ambiguous overloading, or cast the argument topair
.We can simply agree that such examples are sufficiently unlikely in practice, and sufficiently easy to fix, that the benefits of the changed overload set outweigh the costs of running into these examples.
Then I wrote:
I think
priority_queue
should get aninitializer_list
ctor, because we all know whatpriority_queue<int> pq = {1,2,3}
ought to do. I thinkqueue
probably should get aninitializer_list
ctor, because I assume we all know whatqueue<int> q = {1,2,3}
ought to do: items pop from the front of the queue, so "1" would be at the front, right? I'm more skeptical ofstack
. I don't think anyone would guess better than 50/50 whatstack<int> st = {1,2,3}
ought to do, as written. Items pop from the "top" of a stack, yes, but is that the left end or the right end? (Experts know it must be the right end because that's the only efficient end when the container is a vector; but I don't think that's terribly obvious.) However, on the other hand, it's true that the iterator-pair ctor Does The Right Thing: if you push 1, then 2, then 3, you end up with an underlying vector containing{1,2,3}
. So why not just let the programmer write{1,2,3}
in the first place? So I'm skeptical, but not completely anti.Anyway, in all of those cases, adding new ctors will change overload sets — and change them drastically, because
initializer_list
ctors are even greedier than other non-explicit ctors. (This is why implicit conversions are the devil, and the STL's prevailing style of "make everything implicit unless there's a positive reason to make it explicit" is the Wrong Default as usual. Python got it right.) So that's probably why LEWG has been leery of doing so.OTOH,
flat_set
andflat_map
are completely novel class types; nobody has any existing code that would be broken by fiddling with their overload sets. Andflat_set
is supposed to be a drop-in replacement forset
! So obviously it would be a non-starter if you could writestd::set<int> s = {1,2,3};
but not
std::flat_set<int> s = {1,2,3};
That just has to work, period.