C++ Logo

std-proposals

Advanced search

Re: [std-proposals] std::initializer_list constructor for stack and queue

From: Arthur O'Dwyer <arthur.j.odwyer_at_[hidden]>
Date: Thu, 20 Feb 2025 17:01:17 -0500
On Thu, Feb 20, 2025 at 7:16 AM Jonathan Wakely via Std-Proposals <
std-proposals_at_[hidden]> wrote:

> On Thu, 20 Feb 2025 at 11:46, Matthew Satti via Std-Proposals <
> std-proposals_at_[hidden]> wrote:
>
>> Hi there,
>>
>> I was wondering about adding std::initializer_list constructor overloads
>> for std::queue, std::stack, and std::priority_queue. Has there been any
>> proposals regarding this?
>>
>
> It already works with an extra pair of parens:
> std::stack<int> s({1,2,3,4});
> This matches the stack(Container&&) constructor.
>

But if it works with an extra pair of parens, then it should probably also
work without. This seems similar to the situation we were in with
`std::span<const int>` prior to P2447
<https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2023/p2447r6.html>:
that `{{1,2,3}}` worked but `{1,2,3}` didn't.

Now, *originally*, in the C++11 cycle, I believe there was a strong
rationale why
    std::stack<int> s = {1,2,3};
was not made to Just Work. The reason is that it's not obvious whether this
should put 1 on the top of the stack and 3 on the bottom, or vice versa. As
a programmer, I would naturally assume that the "first" element of the
stack's initializer list would be the "first" to pop; but it turns out that
in fact we push and pop from the *back* of a stack, not the front, so this
initialization would actually put 3 on the top of the stack and 1 on the
bottom. This is confusing for at least 50% of the population. So we wisely
designed std::stack *not* to be initializable from an initializer list. For
the same reason, we didn't allow this to work:
    int a[] = {1,2,3};
    std::stack<int> s(a, a+3);
because again it was unclear what order this would actually leave the
elements in. (Although admittedly here it's a little more obvious,
because of how C++ iterators work, that the only sane/implementable
behavior is "push 1, then push 2, then push 3," leaving 3 at the top of the
stack.)

*C++23 added* range constructors and iterator-pair constructors:
- https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2021/p1425r4.pdf
- https://en.cppreference.com/w/cpp/container/stack/stack
This blew the old rationale out of the water. (Wrongly, IMHO, as the old
rationale still applied; but it's too late to debate now.)

So at this point I agree with Matthew: there's no good reason for C++ to
permit
    int a[] = {1,2,3};
    std::stack<int> s(a, a+3);
but to forbid the direct initialization
    std::stack<int> s = {1,2,3};

It would be reasonable to bring a proposal for that. However, I could see a
counterargument that we should "salvage what we can" — we should never have
added the iterator-pair constructor, but at least we can avoid *compounding*
that error at this point. Anyone bringing a proposal for
`stack(initializer_list)` should make sure to robustly discuss that
objection in their paper.

my $.02,
–Arthur

Received on 2025-02-20 22:01:34