C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Missing overloaded constructors for std::initializer_list in some container adapters

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Sat, 11 Mar 2023 08:40:23 -0500
On Sat, Mar 11, 2023 at 7:34 AM LoS via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> I noticed that C++23 added new overloaded constructors for the std::stack
> and std::queue container adapters, that allow to construct the underlying
> container with the contents of the range [first, last). [...]
> However, C++23 also added other container adapters (std::flat_set,
> std::flat_multiset, std::flat_map, std::flat_multimap), that provides the
> following overloaded constructors:
> [...]
> template<class Allocator>
> /* flat_container */ (initializer_list<key_type> il, const Allocator& a);
>
> I did not find a specific reason therefore the overloaded constructors
> were introduced in these group of container adapters but not in the others.
>

Coincidentally, I just mentioned the difficulty of adding non-explicit ctor
overloads yesterday (to container adaptors, no less) in
https://quuxplusone.github.io/draft/d2447-std-span-over-initializer-list.html#annex-c

I think priority_queue *should* get an initializer_list ctor, because we
all know what priority_queue<int> pq = {1,2,3} ought to do.
I think queue *probably* should get an initializer_list ctor, because I
assume we all know what queue<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 of `stack`. I don't think anyone would guess better than
50/50 what stack<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` and `flat_map` are completely novel class types; nobody
has any existing code that would be broken by fiddling with their overload
sets.
And `flat_set` is supposed to be a drop-in replacement for `set`! So
obviously it would be a non-starter if you could write
    std::set<int> s = {1,2,3};
but not
    std::flat_set<int> s = {1,2,3};
That just *has* to work, period.

I'd be mildly interested in coauthoring a paper, but I think it would
require you to show actual implementation experience. Consider submitting a
patch against my "Quuxplusone/llvm-project" libc++ fork. ;) That would
demonstrate that the overload set is possible in principle, and that it
doesn't break any libc++ tests. (Or else, it'll show that it *does* break
things, and maybe answer your question even better.)
https://github.com/llvm/llvm-project/compare/main...Quuxplusone:llvm-project:trivially-relocatable

–Arthur

Received on 2023-03-11 13:40:37