libstdc++: constexpr priority_queue
Checks
Commit Message
Hi!
This patch makes std::priority_queue constexpr as part of P3372R3. I
am unsure whether I should add the FTM here; since the constexpr
support for std::queue isn't complete yet, I have omitted the FTM from
this patch.
Tested on x86_64-linux. OK for trunk?
Additionally, regarding the implementation strategy for other standard
containers like std::list, it seems inevitable that we will need to
move some function implementations (like _List_node_base) from the .cc
files to the headers. This looks like an ABI break. Is this
acceptable?
Best regards,
Yuao
From c4df4c9bced1407499be8c12438ebf1b7c59fca6 Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
Date: Tue, 2 Jun 2026 22:49:19 +0800
Subject: [PATCH] libstdc++: constexpr priority_queue
This patch makes priority_queue constexpr as part of P3372R3.
libstdc++-v3/ChangeLog:
* include/bits/stl_queue.h: Add constexpr.
* testsuite/23_containers/priority_queue/constexpr.cc: New test.
---
libstdc++-v3/include/bits/stl_queue.h | 36 +++++++++++++++-
.../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
2 files changed, 78 insertions(+), 1 deletion(-)
create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
Comments
On Tue, 2 Jun 2026 at 15:59, Yuao Ma <addr2line@gmail.com> wrote:
>
> Hi!
>
> This patch makes std::priority_queue constexpr as part of P3372R3. I
> am unsure whether I should add the FTM here; since the constexpr
> support for std::queue isn't complete yet, I have omitted the FTM from
> this patch.
Yes, that's the correct choice, thanks.
> Tested on x86_64-linux. OK for trunk?
>
> Additionally, regarding the implementation strategy for other standard
> containers like std::list, it seems inevitable that we will need to
> move some function implementations (like _List_node_base) from the .cc
> files to the headers. This looks like an ABI break. Is this
> acceptable?
No. It needs to be done without ABI breaks (which is possible, but
awkward and complex).
On Tue, 02 Jun 2026 at 22:59 +0800, Yuao Ma wrote:
>Hi!
>
>This patch makes std::priority_queue constexpr as part of P3372R3. I
>am unsure whether I should add the FTM here; since the constexpr
>support for std::queue isn't complete yet, I have omitted the FTM from
>this patch.
>
>Tested on x86_64-linux. OK for trunk?
>
>Additionally, regarding the implementation strategy for other standard
>containers like std::list, it seems inevitable that we will need to
>move some function implementations (like _List_node_base) from the .cc
>files to the headers. This looks like an ABI break. Is this
>acceptable?
>
>Best regards,
>Yuao
>From c4df4c9bced1407499be8c12438ebf1b7c59fca6 Mon Sep 17 00:00:00 2001
>From: Yuao Ma <c8ef@outlook.com>
>Date: Tue, 2 Jun 2026 22:49:19 +0800
>Subject: [PATCH] libstdc++: constexpr priority_queue
>
>This patch makes priority_queue constexpr as part of P3372R3.
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/stl_queue.h: Add constexpr.
> * testsuite/23_containers/priority_queue/constexpr.cc: New test.
>---
> libstdc++-v3/include/bits/stl_queue.h | 36 +++++++++++++++-
> .../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
> 2 files changed, 78 insertions(+), 1 deletion(-)
> create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>
>diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
>index 5dc846e13e1..d00284277a9 100644
>--- a/libstdc++-v3/include/bits/stl_queue.h
>+++ b/libstdc++-v3/include/bits/stl_queue.h
>@@ -607,28 +607,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _Seq = _Sequence, typename _Requires = typename
> enable_if<__and_<is_default_constructible<_Compare>,
> is_default_constructible<_Seq>>::value>::type>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue()
> : c(), comp() { }
>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Compare& __x, const _Sequence& __s)
> : c(__s), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
> : c(std::move(__s)), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const priority_queue&) = default;
>+ _GLIBCXX26_CONSTEXPR
These two changes are not needed. The defaulted special members will
be constexpr by default.
> priority_queue& operator=(const priority_queue&) = default;
>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(priority_queue&& __q)
> noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
> is_nothrow_move_constructible<_Compare>>::value)
> : c(std::move(__q.c)), comp(std::move(__q.comp))
> { __q.c.clear(); }
>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue&
> operator=(priority_queue&& __q)
> noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
>@@ -641,32 +648,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Alloc& __a)
> : c(__a), comp() { }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, const _Alloc& __a)
> : c(__a), comp(__x) { }
>
> // _GLIBCXX_RESOLVE_LIB_DEFECTS
> // 2537. Constructors [...] taking allocators should call make_heap
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, const _Sequence& __c,
> const _Alloc& __a)
> : c(__c, __a), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
> : c(std::move(__c), __a), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const priority_queue& __q, const _Alloc& __a)
> : c(__q.c, __a), comp(__q.comp) { }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(priority_queue&& __q, const _Alloc& __a)
> : c(std::move(__q.c), __a), comp(std::move(__q.comp))
> { __q.c.clear(); }
>@@ -704,6 +717,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // 3529. priority_queue(first, last) should construct c with (first, last)
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x = _Compare())
> : c(__first, __last), comp(__x)
>@@ -713,6 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // 3522. Missing requirement on InputIterator template parameter
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Sequence& __s)
> : c(__s), comp(__x)
>@@ -724,6 +739,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, _Sequence&& __s)
> : c(std::move(__s)), comp(__x)
>@@ -741,6 +757,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Alloc& __alloc)
> : c(__first, __last, __alloc), comp()
>@@ -749,6 +766,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Alloc& __alloc)
> : c(__first, __last, __alloc), comp(__x)
>@@ -757,6 +775,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Sequence& __s,
> const _Alloc& __alloc)
>@@ -769,6 +788,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> template<typename _InputIterator, typename _Alloc,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, _Sequence&& __s,
> const _Alloc& __alloc)
>@@ -788,18 +808,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * @{
> */
> template<__detail::__container_compatible_range<_Tp> _Rg>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg,
> const _Compare& __x = _Compare())
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
> const _Alloc& __a)
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
> { std::make_heap(c.begin(), c.end(), comp); }
>@@ -809,12 +832,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> /**
> * Returns true if the %queue is empty.
> */
>- _GLIBCXX_NODISCARD bool
>+ _GLIBCXX_NODISCARD
>+ _GLIBCXX26_CONSTEXPR
Please put the two SHOUTING MACROS on the same line.
>+ bool
> empty() const
> { return c.empty(); }
>
> /** Returns the number of elements in the %queue. */
> _GLIBCXX_NODISCARD
>+ _GLIBCXX26_CONSTEXPR
Same here.
> size_type
> size() const
> { return c.size(); }
>@@ -824,6 +850,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * element of the %queue.
> */
> _GLIBCXX_NODISCARD
>+ _GLIBCXX26_CONSTEXPR
And here.
> const_reference
> top() const
> {
>@@ -839,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * The time complexity of the operation depends on the underlying
> * sequence.
> */
>+ _GLIBCXX26_CONSTEXPR
> void
> push(const value_type& __x)
> {
>@@ -847,6 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> #if __cplusplus >= 201103L
>+ _GLIBCXX26_CONSTEXPR
> void
> push(value_type&& __x)
> {
>@@ -855,6 +884,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> template<typename... _Args>
>+ _GLIBCXX26_CONSTEXPR
> void
> emplace(_Args&&... __args)
> {
>@@ -865,6 +895,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #if __glibcxx_containers_ranges // C++ >= 23
> template<__detail::__container_compatible_range<_Tp> _Rg>
>+ _GLIBCXX26_CONSTEXPR
> void
> push_range(_Rg&& __rg)
> {
>@@ -887,6 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * data is needed, it should be retrieved before pop() is
> * called.
> */
>+ _GLIBCXX26_CONSTEXPR
> void
> pop()
> {
>@@ -896,6 +928,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> #if __cplusplus >= 201103L
>+ _GLIBCXX26_CONSTEXPR
> void
> swap(priority_queue& __pq)
> noexcept(__and_<
>@@ -964,6 +997,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #if __cplusplus >= 201103L
> template<typename _Tp, typename _Sequence, typename _Compare>
>+ _GLIBCXX26_CONSTEXPR
> inline
> #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
> // Constrained free swap overload, see p0185r1
>diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>new file mode 100644
>index 00000000000..fe85617416d
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>@@ -0,0 +1,43 @@
>+// { dg-do compile { target c++26 } }
>+#include <queue>
>+
>+constexpr bool test_constexpr_priority_queue() {
>+ std::priority_queue<int> pq;
>+ if (!pq.empty()) return false;
>+ if (pq.size() != 0) return false;
>+
>+ pq.push(10);
>+ if (pq.empty()) return false;
>+ if (pq.size() != 1) return false;
>+ if (pq.top() != 10) return false;
>+
>+ pq.emplace(30);
>+ pq.emplace(20);
>+ if (pq.size() != 3) return false;
>+ if (pq.top() != 30) return false;
>+
>+ pq.pop();
>+ if (pq.size() != 2) return false;
>+ if (pq.top() != 20) return false;
>+
>+ std::priority_queue<int> pq2;
>+ pq2 = pq;
>+ if (pq2.size() != 2) return false;
>+ if (pq2.top() != 20) return false;
>+
>+ std::priority_queue<int> pq3;
>+ pq3.push(100);
>+ pq.swap(pq3);
>+ if (pq.size() != 1 || pq.top() != 100) return false;
>+ if (pq3.size() != 2 || pq3.top() != 20) return false;
>+
>+ int arr[] = {50, 200, 10};
>+ pq.push_range(arr);
>+ if (pq.size() != 4) return false;
>+ if (pq.top() != 200) return false;
>+
>+ return true;
>+}
>+
>+static_assert(test_constexpr_priority_queue(),
>+ "constexpr priority_queue test failed!");
>--
>2.53.0
>
On Tue, Jun 2, 2026 at 11:08 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Tue, 02 Jun 2026 at 22:59 +0800, Yuao Ma wrote:
> >Hi!
> >
> >This patch makes std::priority_queue constexpr as part of P3372R3. I
> >am unsure whether I should add the FTM here; since the constexpr
> >support for std::queue isn't complete yet, I have omitted the FTM from
> >this patch.
> >
> >Tested on x86_64-linux. OK for trunk?
> >
> >Additionally, regarding the implementation strategy for other standard
> >containers like std::list, it seems inevitable that we will need to
> >move some function implementations (like _List_node_base) from the .cc
> >files to the headers. This looks like an ABI break. Is this
> >acceptable?
> >
> >Best regards,
> >Yuao
>
> >From c4df4c9bced1407499be8c12438ebf1b7c59fca6 Mon Sep 17 00:00:00 2001
> >From: Yuao Ma <c8ef@outlook.com>
> >Date: Tue, 2 Jun 2026 22:49:19 +0800
> >Subject: [PATCH] libstdc++: constexpr priority_queue
> >
> >This patch makes priority_queue constexpr as part of P3372R3.
> >
> >libstdc++-v3/ChangeLog:
> >
> > * include/bits/stl_queue.h: Add constexpr.
> > * testsuite/23_containers/priority_queue/constexpr.cc: New test.
> >---
> > libstdc++-v3/include/bits/stl_queue.h | 36 +++++++++++++++-
> > .../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
> > 2 files changed, 78 insertions(+), 1 deletion(-)
> > create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
> >
> >diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
> >index 5dc846e13e1..d00284277a9 100644
> >--- a/libstdc++-v3/include/bits/stl_queue.h
> >+++ b/libstdc++-v3/include/bits/stl_queue.h
> >@@ -607,28 +607,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > template<typename _Seq = _Sequence, typename _Requires = typename
> > enable_if<__and_<is_default_constructible<_Compare>,
> > is_default_constructible<_Seq>>::value>::type>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue()
> > : c(), comp() { }
> >
> >+ _GLIBCXX26_CONSTEXPR
> > explicit
> > priority_queue(const _Compare& __x, const _Sequence& __s)
> > : c(__s), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> >+ _GLIBCXX26_CONSTEXPR
> > explicit
> > priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
> > : c(std::move(__s)), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(const priority_queue&) = default;
> >+ _GLIBCXX26_CONSTEXPR
>
> These two changes are not needed. The defaulted special members will
> be constexpr by default.
>
Fixed.
> > priority_queue& operator=(const priority_queue&) = default;
> >
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(priority_queue&& __q)
> > noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
> > is_nothrow_move_constructible<_Compare>>::value)
> > : c(std::move(__q.c)), comp(std::move(__q.comp))
> > { __q.c.clear(); }
> >
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue&
> > operator=(priority_queue&& __q)
> > noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
> >@@ -641,32 +648,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > }
> >
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > explicit
> > priority_queue(const _Alloc& __a)
> > : c(__a), comp() { }
> >
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(const _Compare& __x, const _Alloc& __a)
> > : c(__a), comp(__x) { }
> >
> > // _GLIBCXX_RESOLVE_LIB_DEFECTS
> > // 2537. Constructors [...] taking allocators should call make_heap
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(const _Compare& __x, const _Sequence& __c,
> > const _Alloc& __a)
> > : c(__c, __a), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
> > : c(std::move(__c), __a), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(const priority_queue& __q, const _Alloc& __a)
> > : c(__q.c, __a), comp(__q.comp) { }
> >
> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(priority_queue&& __q, const _Alloc& __a)
> > : c(std::move(__q.c), __a), comp(std::move(__q.comp))
> > { __q.c.clear(); }
> >@@ -704,6 +717,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > // 3529. priority_queue(first, last) should construct c with (first, last)
> > template<typename _InputIterator,
> > typename = std::_RequireInputIter<_InputIterator>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x = _Compare())
> > : c(__first, __last), comp(__x)
> >@@ -713,6 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > // 3522. Missing requirement on InputIterator template parameter
> > template<typename _InputIterator,
> > typename = std::_RequireInputIter<_InputIterator>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x, const _Sequence& __s)
> > : c(__s), comp(__x)
> >@@ -724,6 +739,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > template<typename _InputIterator,
> > typename = std::_RequireInputIter<_InputIterator>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x, _Sequence&& __s)
> > : c(std::move(__s)), comp(__x)
> >@@ -741,6 +757,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > template<typename _InputIterator, typename _Alloc,
> > typename = std::_RequireInputIter<_InputIterator>,
> > typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Alloc& __alloc)
> > : c(__first, __last, __alloc), comp()
> >@@ -749,6 +766,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > template<typename _InputIterator, typename _Alloc,
> > typename = std::_RequireInputIter<_InputIterator>,
> > typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x, const _Alloc& __alloc)
> > : c(__first, __last, __alloc), comp(__x)
> >@@ -757,6 +775,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > template<typename _InputIterator, typename _Alloc,
> > typename = std::_RequireInputIter<_InputIterator>,
> > typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x, const _Sequence& __s,
> > const _Alloc& __alloc)
> >@@ -769,6 +788,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > template<typename _InputIterator, typename _Alloc,
> > typename _Requires = _Uses<_Alloc>>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(_InputIterator __first, _InputIterator __last,
> > const _Compare& __x, _Sequence&& __s,
> > const _Alloc& __alloc)
> >@@ -788,18 +808,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > * @{
> > */
> > template<__detail::__container_compatible_range<_Tp> _Rg>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(from_range_t, _Rg&& __rg,
> > const _Compare& __x = _Compare())
> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> > template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
> > const _Alloc& __a)
> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
> > { std::make_heap(c.begin(), c.end(), comp); }
> >
> > template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
> >+ _GLIBCXX26_CONSTEXPR
> > priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
> > { std::make_heap(c.begin(), c.end(), comp); }
> >@@ -809,12 +832,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > /**
> > * Returns true if the %queue is empty.
> > */
> >- _GLIBCXX_NODISCARD bool
> >+ _GLIBCXX_NODISCARD
> >+ _GLIBCXX26_CONSTEXPR
>
> Please put the two SHOUTING MACROS on the same line.
>
Fixed.
> >+ bool
> > empty() const
> > { return c.empty(); }
> >
> > /** Returns the number of elements in the %queue. */
> > _GLIBCXX_NODISCARD
> >+ _GLIBCXX26_CONSTEXPR
>
> Same here.
>
Fixed.
> > size_type
> > size() const
> > { return c.size(); }
> >@@ -824,6 +850,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > * element of the %queue.
> > */
> > _GLIBCXX_NODISCARD
> >+ _GLIBCXX26_CONSTEXPR
>
> And here.
>
Fixed.
> > const_reference
> > top() const
> > {
> >@@ -839,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > * The time complexity of the operation depends on the underlying
> > * sequence.
> > */
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > push(const value_type& __x)
> > {
> >@@ -847,6 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > }
> >
> > #if __cplusplus >= 201103L
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > push(value_type&& __x)
> > {
> >@@ -855,6 +884,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > }
> >
> > template<typename... _Args>
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > emplace(_Args&&... __args)
> > {
> >@@ -865,6 +895,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > #if __glibcxx_containers_ranges // C++ >= 23
> > template<__detail::__container_compatible_range<_Tp> _Rg>
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > push_range(_Rg&& __rg)
> > {
> >@@ -887,6 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > * data is needed, it should be retrieved before pop() is
> > * called.
> > */
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > pop()
> > {
> >@@ -896,6 +928,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> > }
> >
> > #if __cplusplus >= 201103L
> >+ _GLIBCXX26_CONSTEXPR
> > void
> > swap(priority_queue& __pq)
> > noexcept(__and_<
> >@@ -964,6 +997,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> >
> > #if __cplusplus >= 201103L
> > template<typename _Tp, typename _Sequence, typename _Compare>
> >+ _GLIBCXX26_CONSTEXPR
> > inline
> > #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
> > // Constrained free swap overload, see p0185r1
> >diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
> >new file mode 100644
> >index 00000000000..fe85617416d
> >--- /dev/null
> >+++ b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
> >@@ -0,0 +1,43 @@
> >+// { dg-do compile { target c++26 } }
> >+#include <queue>
> >+
> >+constexpr bool test_constexpr_priority_queue() {
> >+ std::priority_queue<int> pq;
> >+ if (!pq.empty()) return false;
> >+ if (pq.size() != 0) return false;
> >+
> >+ pq.push(10);
> >+ if (pq.empty()) return false;
> >+ if (pq.size() != 1) return false;
> >+ if (pq.top() != 10) return false;
> >+
> >+ pq.emplace(30);
> >+ pq.emplace(20);
> >+ if (pq.size() != 3) return false;
> >+ if (pq.top() != 30) return false;
> >+
> >+ pq.pop();
> >+ if (pq.size() != 2) return false;
> >+ if (pq.top() != 20) return false;
> >+
> >+ std::priority_queue<int> pq2;
> >+ pq2 = pq;
> >+ if (pq2.size() != 2) return false;
> >+ if (pq2.top() != 20) return false;
> >+
> >+ std::priority_queue<int> pq3;
> >+ pq3.push(100);
> >+ pq.swap(pq3);
> >+ if (pq.size() != 1 || pq.top() != 100) return false;
> >+ if (pq3.size() != 2 || pq3.top() != 20) return false;
> >+
> >+ int arr[] = {50, 200, 10};
> >+ pq.push_range(arr);
> >+ if (pq.size() != 4) return false;
> >+ if (pq.top() != 200) return false;
> >+
> >+ return true;
> >+}
> >+
> >+static_assert(test_constexpr_priority_queue(),
> >+ "constexpr priority_queue test failed!");
> >--
> >2.53.0
> >
>
Thanks for the quick review! The new patch is attached below.
From 790166da292c8bac878afee26f619f8a31af9072 Mon Sep 17 00:00:00 2001
From: Yuao Ma <c8ef@outlook.com>
Date: Tue, 2 Jun 2026 23:18:26 +0800
Subject: [PATCH] libstdc++: constexpr priority_queue
This patch makes priority_queue constexpr as part of P3372R3.
libstdc++-v3/ChangeLog:
* include/bits/stl_queue.h: Add constexpr.
* testsuite/23_containers/priority_queue/constexpr.cc: New test.
---
libstdc++-v3/include/bits/stl_queue.h | 35 +++++++++++++--
.../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
2 files changed, 75 insertions(+), 3 deletions(-)
create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
index 5dc846e13e1..d9edc4c838d 100644
--- a/libstdc++-v3/include/bits/stl_queue.h
+++ b/libstdc++-v3/include/bits/stl_queue.h
@@ -607,14 +607,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Seq = _Sequence, typename _Requires = typename
enable_if<__and_<is_default_constructible<_Compare>,
is_default_constructible<_Seq>>::value>::type>
+ _GLIBCXX26_CONSTEXPR
priority_queue()
: c(), comp() { }
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
@@ -623,12 +626,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
priority_queue(const priority_queue&) = default;
priority_queue& operator=(const priority_queue&) = default;
+ _GLIBCXX26_CONSTEXPR
priority_queue(priority_queue&& __q)
noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
is_nothrow_move_constructible<_Compare>>::value)
: c(std::move(__q.c)), comp(std::move(__q.comp))
{ __q.c.clear(); }
+ _GLIBCXX26_CONSTEXPR
priority_queue&
operator=(priority_queue&& __q)
noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
@@ -641,32 +646,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Alloc& __a)
: c(__a), comp() { }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, const _Alloc& __a)
: c(__a), comp(__x) { }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2537. Constructors [...] taking allocators should call make_heap
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, const _Sequence& __c,
const _Alloc& __a)
: c(__c, __a), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
: c(std::move(__c), __a), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const priority_queue& __q, const _Alloc& __a)
: c(__q.c, __a), comp(__q.comp) { }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(priority_queue&& __q, const _Alloc& __a)
: c(std::move(__q.c), __a), comp(std::move(__q.comp))
{ __q.c.clear(); }
@@ -704,6 +715,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 3529. priority_queue(first, last) should construct c with (first, last)
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x = _Compare())
: c(__first, __last), comp(__x)
@@ -713,6 +725,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 3522. Missing requirement on InputIterator template parameter
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
@@ -724,6 +737,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, _Sequence&& __s)
: c(std::move(__s)), comp(__x)
@@ -741,6 +755,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Alloc& __alloc)
: c(__first, __last, __alloc), comp()
@@ -749,6 +764,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Alloc& __alloc)
: c(__first, __last, __alloc), comp(__x)
@@ -757,6 +773,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Sequence& __s,
const _Alloc& __alloc)
@@ -769,6 +786,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, _Sequence&& __s,
const _Alloc& __alloc)
@@ -788,18 +806,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @{
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg,
const _Compare& __x = _Compare())
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
const _Alloc& __a)
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
{ std::make_heap(c.begin(), c.end(), comp); }
@@ -809,12 +830,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* Returns true if the %queue is empty.
*/
- _GLIBCXX_NODISCARD bool
+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
+ bool
empty() const
{ return c.empty(); }
/** Returns the number of elements in the %queue. */
- _GLIBCXX_NODISCARD
+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
size_type
size() const
{ return c.size(); }
@@ -823,7 +845,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* Returns a read-only (constant) reference to the data at the first
* element of the %queue.
*/
- _GLIBCXX_NODISCARD
+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
const_reference
top() const
{
@@ -839,6 +861,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* The time complexity of the operation depends on the underlying
* sequence.
*/
+ _GLIBCXX26_CONSTEXPR
void
push(const value_type& __x)
{
@@ -847,6 +870,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#if __cplusplus >= 201103L
+ _GLIBCXX26_CONSTEXPR
void
push(value_type&& __x)
{
@@ -855,6 +879,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _Args>
+ _GLIBCXX26_CONSTEXPR
void
emplace(_Args&&... __args)
{
@@ -865,6 +890,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __glibcxx_containers_ranges // C++ >= 23
template<__detail::__container_compatible_range<_Tp> _Rg>
+ _GLIBCXX26_CONSTEXPR
void
push_range(_Rg&& __rg)
{
@@ -887,6 +913,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* data is needed, it should be retrieved before pop() is
* called.
*/
+ _GLIBCXX26_CONSTEXPR
void
pop()
{
@@ -896,6 +923,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#if __cplusplus >= 201103L
+ _GLIBCXX26_CONSTEXPR
void
swap(priority_queue& __pq)
noexcept(__and_<
@@ -964,6 +992,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201103L
template<typename _Tp, typename _Sequence, typename _Compare>
+ _GLIBCXX26_CONSTEXPR
inline
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
// Constrained free swap overload, see p0185r1
diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
new file mode 100644
index 00000000000..fe85617416d
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
@@ -0,0 +1,43 @@
+// { dg-do compile { target c++26 } }
+#include <queue>
+
+constexpr bool test_constexpr_priority_queue() {
+ std::priority_queue<int> pq;
+ if (!pq.empty()) return false;
+ if (pq.size() != 0) return false;
+
+ pq.push(10);
+ if (pq.empty()) return false;
+ if (pq.size() != 1) return false;
+ if (pq.top() != 10) return false;
+
+ pq.emplace(30);
+ pq.emplace(20);
+ if (pq.size() != 3) return false;
+ if (pq.top() != 30) return false;
+
+ pq.pop();
+ if (pq.size() != 2) return false;
+ if (pq.top() != 20) return false;
+
+ std::priority_queue<int> pq2;
+ pq2 = pq;
+ if (pq2.size() != 2) return false;
+ if (pq2.top() != 20) return false;
+
+ std::priority_queue<int> pq3;
+ pq3.push(100);
+ pq.swap(pq3);
+ if (pq.size() != 1 || pq.top() != 100) return false;
+ if (pq3.size() != 2 || pq3.top() != 20) return false;
+
+ int arr[] = {50, 200, 10};
+ pq.push_range(arr);
+ if (pq.size() != 4) return false;
+ if (pq.top() != 200) return false;
+
+ return true;
+}
+
+static_assert(test_constexpr_priority_queue(),
+ "constexpr priority_queue test failed!");
On Tue, 02 Jun 2026 at 23:20 +0800, Yuao Ma wrote:
>On Tue, Jun 2, 2026 at 11:08 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>>
>> On Tue, 02 Jun 2026 at 22:59 +0800, Yuao Ma wrote:
>> >Hi!
>> >
>> >This patch makes std::priority_queue constexpr as part of P3372R3. I
>> >am unsure whether I should add the FTM here; since the constexpr
>> >support for std::queue isn't complete yet, I have omitted the FTM from
>> >this patch.
>> >
>> >Tested on x86_64-linux. OK for trunk?
>> >
>> >Additionally, regarding the implementation strategy for other standard
>> >containers like std::list, it seems inevitable that we will need to
>> >move some function implementations (like _List_node_base) from the .cc
>> >files to the headers. This looks like an ABI break. Is this
>> >acceptable?
>> >
>> >Best regards,
>> >Yuao
>>
>> >From c4df4c9bced1407499be8c12438ebf1b7c59fca6 Mon Sep 17 00:00:00 2001
>> >From: Yuao Ma <c8ef@outlook.com>
>> >Date: Tue, 2 Jun 2026 22:49:19 +0800
>> >Subject: [PATCH] libstdc++: constexpr priority_queue
>> >
>> >This patch makes priority_queue constexpr as part of P3372R3.
>> >
>> >libstdc++-v3/ChangeLog:
>> >
>> > * include/bits/stl_queue.h: Add constexpr.
>> > * testsuite/23_containers/priority_queue/constexpr.cc: New test.
>> >---
>> > libstdc++-v3/include/bits/stl_queue.h | 36 +++++++++++++++-
>> > .../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
>> > 2 files changed, 78 insertions(+), 1 deletion(-)
>> > create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>> >
>> >diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
>> >index 5dc846e13e1..d00284277a9 100644
>> >--- a/libstdc++-v3/include/bits/stl_queue.h
>> >+++ b/libstdc++-v3/include/bits/stl_queue.h
>> >@@ -607,28 +607,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > template<typename _Seq = _Sequence, typename _Requires = typename
>> > enable_if<__and_<is_default_constructible<_Compare>,
>> > is_default_constructible<_Seq>>::value>::type>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue()
>> > : c(), comp() { }
>> >
>> >+ _GLIBCXX26_CONSTEXPR
>> > explicit
>> > priority_queue(const _Compare& __x, const _Sequence& __s)
>> > : c(__s), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> >+ _GLIBCXX26_CONSTEXPR
>> > explicit
>> > priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
>> > : c(std::move(__s)), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(const priority_queue&) = default;
>> >+ _GLIBCXX26_CONSTEXPR
>>
>> These two changes are not needed. The defaulted special members will
>> be constexpr by default.
>>
>
>Fixed.
>
>> > priority_queue& operator=(const priority_queue&) = default;
>> >
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(priority_queue&& __q)
>> > noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
>> > is_nothrow_move_constructible<_Compare>>::value)
>> > : c(std::move(__q.c)), comp(std::move(__q.comp))
>> > { __q.c.clear(); }
>> >
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue&
>> > operator=(priority_queue&& __q)
>> > noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
>> >@@ -641,32 +648,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > }
>> >
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > explicit
>> > priority_queue(const _Alloc& __a)
>> > : c(__a), comp() { }
>> >
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(const _Compare& __x, const _Alloc& __a)
>> > : c(__a), comp(__x) { }
>> >
>> > // _GLIBCXX_RESOLVE_LIB_DEFECTS
>> > // 2537. Constructors [...] taking allocators should call make_heap
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(const _Compare& __x, const _Sequence& __c,
>> > const _Alloc& __a)
>> > : c(__c, __a), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
>> > : c(std::move(__c), __a), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(const priority_queue& __q, const _Alloc& __a)
>> > : c(__q.c, __a), comp(__q.comp) { }
>> >
>> > template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(priority_queue&& __q, const _Alloc& __a)
>> > : c(std::move(__q.c), __a), comp(std::move(__q.comp))
>> > { __q.c.clear(); }
>> >@@ -704,6 +717,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > // 3529. priority_queue(first, last) should construct c with (first, last)
>> > template<typename _InputIterator,
>> > typename = std::_RequireInputIter<_InputIterator>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x = _Compare())
>> > : c(__first, __last), comp(__x)
>> >@@ -713,6 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > // 3522. Missing requirement on InputIterator template parameter
>> > template<typename _InputIterator,
>> > typename = std::_RequireInputIter<_InputIterator>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x, const _Sequence& __s)
>> > : c(__s), comp(__x)
>> >@@ -724,6 +739,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >
>> > template<typename _InputIterator,
>> > typename = std::_RequireInputIter<_InputIterator>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x, _Sequence&& __s)
>> > : c(std::move(__s)), comp(__x)
>> >@@ -741,6 +757,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > template<typename _InputIterator, typename _Alloc,
>> > typename = std::_RequireInputIter<_InputIterator>,
>> > typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Alloc& __alloc)
>> > : c(__first, __last, __alloc), comp()
>> >@@ -749,6 +766,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > template<typename _InputIterator, typename _Alloc,
>> > typename = std::_RequireInputIter<_InputIterator>,
>> > typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x, const _Alloc& __alloc)
>> > : c(__first, __last, __alloc), comp(__x)
>> >@@ -757,6 +775,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > template<typename _InputIterator, typename _Alloc,
>> > typename = std::_RequireInputIter<_InputIterator>,
>> > typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x, const _Sequence& __s,
>> > const _Alloc& __alloc)
>> >@@ -769,6 +788,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >
>> > template<typename _InputIterator, typename _Alloc,
>> > typename _Requires = _Uses<_Alloc>>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(_InputIterator __first, _InputIterator __last,
>> > const _Compare& __x, _Sequence&& __s,
>> > const _Alloc& __alloc)
>> >@@ -788,18 +808,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > * @{
>> > */
>> > template<__detail::__container_compatible_range<_Tp> _Rg>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(from_range_t, _Rg&& __rg,
>> > const _Compare& __x = _Compare())
>> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> > template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
>> > const _Alloc& __a)
>> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >
>> > template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>> >+ _GLIBCXX26_CONSTEXPR
>> > priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
>> > : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
>> > { std::make_heap(c.begin(), c.end(), comp); }
>> >@@ -809,12 +832,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > /**
>> > * Returns true if the %queue is empty.
>> > */
>> >- _GLIBCXX_NODISCARD bool
>> >+ _GLIBCXX_NODISCARD
>> >+ _GLIBCXX26_CONSTEXPR
>>
>> Please put the two SHOUTING MACROS on the same line.
>>
>
>Fixed.
>
>> >+ bool
>> > empty() const
>> > { return c.empty(); }
>> >
>> > /** Returns the number of elements in the %queue. */
>> > _GLIBCXX_NODISCARD
>> >+ _GLIBCXX26_CONSTEXPR
>>
>> Same here.
>>
>
>Fixed.
>
>> > size_type
>> > size() const
>> > { return c.size(); }
>> >@@ -824,6 +850,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > * element of the %queue.
>> > */
>> > _GLIBCXX_NODISCARD
>> >+ _GLIBCXX26_CONSTEXPR
>>
>> And here.
>>
>
>Fixed.
>
>> > const_reference
>> > top() const
>> > {
>> >@@ -839,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > * The time complexity of the operation depends on the underlying
>> > * sequence.
>> > */
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > push(const value_type& __x)
>> > {
>> >@@ -847,6 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > }
>> >
>> > #if __cplusplus >= 201103L
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > push(value_type&& __x)
>> > {
>> >@@ -855,6 +884,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > }
>> >
>> > template<typename... _Args>
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > emplace(_Args&&... __args)
>> > {
>> >@@ -865,6 +895,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >
>> > #if __glibcxx_containers_ranges // C++ >= 23
>> > template<__detail::__container_compatible_range<_Tp> _Rg>
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > push_range(_Rg&& __rg)
>> > {
>> >@@ -887,6 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > * data is needed, it should be retrieved before pop() is
>> > * called.
>> > */
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > pop()
>> > {
>> >@@ -896,6 +928,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> > }
>> >
>> > #if __cplusplus >= 201103L
>> >+ _GLIBCXX26_CONSTEXPR
>> > void
>> > swap(priority_queue& __pq)
>> > noexcept(__and_<
>> >@@ -964,6 +997,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>> >
>> > #if __cplusplus >= 201103L
>> > template<typename _Tp, typename _Sequence, typename _Compare>
>> >+ _GLIBCXX26_CONSTEXPR
>> > inline
>> > #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
>> > // Constrained free swap overload, see p0185r1
>> >diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>> >new file mode 100644
>> >index 00000000000..fe85617416d
>> >--- /dev/null
>> >+++ b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>> >@@ -0,0 +1,43 @@
>> >+// { dg-do compile { target c++26 } }
>> >+#include <queue>
>> >+
>> >+constexpr bool test_constexpr_priority_queue() {
>> >+ std::priority_queue<int> pq;
>> >+ if (!pq.empty()) return false;
>> >+ if (pq.size() != 0) return false;
>> >+
>> >+ pq.push(10);
>> >+ if (pq.empty()) return false;
>> >+ if (pq.size() != 1) return false;
>> >+ if (pq.top() != 10) return false;
>> >+
>> >+ pq.emplace(30);
>> >+ pq.emplace(20);
>> >+ if (pq.size() != 3) return false;
>> >+ if (pq.top() != 30) return false;
>> >+
>> >+ pq.pop();
>> >+ if (pq.size() != 2) return false;
>> >+ if (pq.top() != 20) return false;
>> >+
>> >+ std::priority_queue<int> pq2;
>> >+ pq2 = pq;
>> >+ if (pq2.size() != 2) return false;
>> >+ if (pq2.top() != 20) return false;
>> >+
>> >+ std::priority_queue<int> pq3;
>> >+ pq3.push(100);
>> >+ pq.swap(pq3);
>> >+ if (pq.size() != 1 || pq.top() != 100) return false;
>> >+ if (pq3.size() != 2 || pq3.top() != 20) return false;
>> >+
>> >+ int arr[] = {50, 200, 10};
>> >+ pq.push_range(arr);
>> >+ if (pq.size() != 4) return false;
>> >+ if (pq.top() != 200) return false;
>> >+
>> >+ return true;
>> >+}
>> >+
>> >+static_assert(test_constexpr_priority_queue(),
>> >+ "constexpr priority_queue test failed!");
>> >--
>> >2.53.0
>> >
>>
>
>Thanks for the quick review! The new patch is attached below.
>From 790166da292c8bac878afee26f619f8a31af9072 Mon Sep 17 00:00:00 2001
>From: Yuao Ma <c8ef@outlook.com>
>Date: Tue, 2 Jun 2026 23:18:26 +0800
>Subject: [PATCH] libstdc++: constexpr priority_queue
>
>This patch makes priority_queue constexpr as part of P3372R3.
>
>libstdc++-v3/ChangeLog:
>
> * include/bits/stl_queue.h: Add constexpr.
> * testsuite/23_containers/priority_queue/constexpr.cc: New test.
OK for trunk, thanks.
>---
> libstdc++-v3/include/bits/stl_queue.h | 35 +++++++++++++--
> .../23_containers/priority_queue/constexpr.cc | 43 +++++++++++++++++++
> 2 files changed, 75 insertions(+), 3 deletions(-)
> create mode 100644 libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>
>diff --git a/libstdc++-v3/include/bits/stl_queue.h b/libstdc++-v3/include/bits/stl_queue.h
>index 5dc846e13e1..d9edc4c838d 100644
>--- a/libstdc++-v3/include/bits/stl_queue.h
>+++ b/libstdc++-v3/include/bits/stl_queue.h
>@@ -607,14 +607,17 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _Seq = _Sequence, typename _Requires = typename
> enable_if<__and_<is_default_constructible<_Compare>,
> is_default_constructible<_Seq>>::value>::type>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue()
> : c(), comp() { }
>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Compare& __x, const _Sequence& __s)
> : c(__s), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
> : c(std::move(__s)), comp(__x)
>@@ -623,12 +626,14 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> priority_queue(const priority_queue&) = default;
> priority_queue& operator=(const priority_queue&) = default;
>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(priority_queue&& __q)
> noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
> is_nothrow_move_constructible<_Compare>>::value)
> : c(std::move(__q.c)), comp(std::move(__q.comp))
> { __q.c.clear(); }
>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue&
> operator=(priority_queue&& __q)
> noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
>@@ -641,32 +646,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> explicit
> priority_queue(const _Alloc& __a)
> : c(__a), comp() { }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, const _Alloc& __a)
> : c(__a), comp(__x) { }
>
> // _GLIBCXX_RESOLVE_LIB_DEFECTS
> // 2537. Constructors [...] taking allocators should call make_heap
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, const _Sequence& __c,
> const _Alloc& __a)
> : c(__c, __a), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
> : c(std::move(__c), __a), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(const priority_queue& __q, const _Alloc& __a)
> : c(__q.c, __a), comp(__q.comp) { }
>
> template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(priority_queue&& __q, const _Alloc& __a)
> : c(std::move(__q.c), __a), comp(std::move(__q.comp))
> { __q.c.clear(); }
>@@ -704,6 +715,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // 3529. priority_queue(first, last) should construct c with (first, last)
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x = _Compare())
> : c(__first, __last), comp(__x)
>@@ -713,6 +725,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> // 3522. Missing requirement on InputIterator template parameter
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Sequence& __s)
> : c(__s), comp(__x)
>@@ -724,6 +737,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> template<typename _InputIterator,
> typename = std::_RequireInputIter<_InputIterator>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, _Sequence&& __s)
> : c(std::move(__s)), comp(__x)
>@@ -741,6 +755,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Alloc& __alloc)
> : c(__first, __last, __alloc), comp()
>@@ -749,6 +764,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Alloc& __alloc)
> : c(__first, __last, __alloc), comp(__x)
>@@ -757,6 +773,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> template<typename _InputIterator, typename _Alloc,
> typename = std::_RequireInputIter<_InputIterator>,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, const _Sequence& __s,
> const _Alloc& __alloc)
>@@ -769,6 +786,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> template<typename _InputIterator, typename _Alloc,
> typename _Requires = _Uses<_Alloc>>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(_InputIterator __first, _InputIterator __last,
> const _Compare& __x, _Sequence&& __s,
> const _Alloc& __alloc)
>@@ -788,18 +806,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * @{
> */
> template<__detail::__container_compatible_range<_Tp> _Rg>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg,
> const _Compare& __x = _Compare())
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
> const _Alloc& __a)
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
> { std::make_heap(c.begin(), c.end(), comp); }
>
> template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
>+ _GLIBCXX26_CONSTEXPR
> priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
> : c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
> { std::make_heap(c.begin(), c.end(), comp); }
>@@ -809,12 +830,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> /**
> * Returns true if the %queue is empty.
> */
>- _GLIBCXX_NODISCARD bool
>+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
>+ bool
> empty() const
> { return c.empty(); }
>
> /** Returns the number of elements in the %queue. */
>- _GLIBCXX_NODISCARD
>+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
> size_type
> size() const
> { return c.size(); }
>@@ -823,7 +845,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * Returns a read-only (constant) reference to the data at the first
> * element of the %queue.
> */
>- _GLIBCXX_NODISCARD
>+ _GLIBCXX_NODISCARD _GLIBCXX26_CONSTEXPR
> const_reference
> top() const
> {
>@@ -839,6 +861,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * The time complexity of the operation depends on the underlying
> * sequence.
> */
>+ _GLIBCXX26_CONSTEXPR
> void
> push(const value_type& __x)
> {
>@@ -847,6 +870,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> #if __cplusplus >= 201103L
>+ _GLIBCXX26_CONSTEXPR
> void
> push(value_type&& __x)
> {
>@@ -855,6 +879,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> template<typename... _Args>
>+ _GLIBCXX26_CONSTEXPR
> void
> emplace(_Args&&... __args)
> {
>@@ -865,6 +890,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #if __glibcxx_containers_ranges // C++ >= 23
> template<__detail::__container_compatible_range<_Tp> _Rg>
>+ _GLIBCXX26_CONSTEXPR
> void
> push_range(_Rg&& __rg)
> {
>@@ -887,6 +913,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> * data is needed, it should be retrieved before pop() is
> * called.
> */
>+ _GLIBCXX26_CONSTEXPR
> void
> pop()
> {
>@@ -896,6 +923,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
> }
>
> #if __cplusplus >= 201103L
>+ _GLIBCXX26_CONSTEXPR
> void
> swap(priority_queue& __pq)
> noexcept(__and_<
>@@ -964,6 +992,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
>
> #if __cplusplus >= 201103L
> template<typename _Tp, typename _Sequence, typename _Compare>
>+ _GLIBCXX26_CONSTEXPR
> inline
> #if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
> // Constrained free swap overload, see p0185r1
>diff --git a/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>new file mode 100644
>index 00000000000..fe85617416d
>--- /dev/null
>+++ b/libstdc++-v3/testsuite/23_containers/priority_queue/constexpr.cc
>@@ -0,0 +1,43 @@
>+// { dg-do compile { target c++26 } }
>+#include <queue>
>+
>+constexpr bool test_constexpr_priority_queue() {
>+ std::priority_queue<int> pq;
>+ if (!pq.empty()) return false;
>+ if (pq.size() != 0) return false;
>+
>+ pq.push(10);
>+ if (pq.empty()) return false;
>+ if (pq.size() != 1) return false;
>+ if (pq.top() != 10) return false;
>+
>+ pq.emplace(30);
>+ pq.emplace(20);
>+ if (pq.size() != 3) return false;
>+ if (pq.top() != 30) return false;
>+
>+ pq.pop();
>+ if (pq.size() != 2) return false;
>+ if (pq.top() != 20) return false;
>+
>+ std::priority_queue<int> pq2;
>+ pq2 = pq;
>+ if (pq2.size() != 2) return false;
>+ if (pq2.top() != 20) return false;
>+
>+ std::priority_queue<int> pq3;
>+ pq3.push(100);
>+ pq.swap(pq3);
>+ if (pq.size() != 1 || pq.top() != 100) return false;
>+ if (pq3.size() != 2 || pq3.top() != 20) return false;
>+
>+ int arr[] = {50, 200, 10};
>+ pq.push_range(arr);
>+ if (pq.size() != 4) return false;
>+ if (pq.top() != 200) return false;
>+
>+ return true;
>+}
>+
>+static_assert(test_constexpr_priority_queue(),
>+ "constexpr priority_queue test failed!");
>--
>2.53.0
>
On Tue, Jun 2, 2026 at 11:01 PM Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Tue, 2 Jun 2026 at 15:59, Yuao Ma <addr2line@gmail.com> wrote:
> >
> > Hi!
> >
> > This patch makes std::priority_queue constexpr as part of P3372R3. I
> > am unsure whether I should add the FTM here; since the constexpr
> > support for std::queue isn't complete yet, I have omitted the FTM from
> > this patch.
>
> Yes, that's the correct choice, thanks.
>
> > Tested on x86_64-linux. OK for trunk?
> >
> > Additionally, regarding the implementation strategy for other standard
> > containers like std::list, it seems inevitable that we will need to
> > move some function implementations (like _List_node_base) from the .cc
> > files to the headers. This looks like an ABI break. Is this
> > acceptable?
>
> No. It needs to be done without ABI breaks (which is possible, but
> awkward and complex).
>
BTW, could you elaborate more on how to achieve this in general? The
only approach I can think of is using attributes like noinline and
default visibility.
On Tue, 2 Jun 2026 at 18:04, Yuao Ma <addr2line@gmail.com> wrote:
>
> On Tue, Jun 2, 2026 at 11:01 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> >
> > On Tue, 2 Jun 2026 at 15:59, Yuao Ma <addr2line@gmail.com> wrote:
> > >
> > > Hi!
> > >
> > > This patch makes std::priority_queue constexpr as part of P3372R3. I
> > > am unsure whether I should add the FTM here; since the constexpr
> > > support for std::queue isn't complete yet, I have omitted the FTM from
> > > this patch.
> >
> > Yes, that's the correct choice, thanks.
> >
> > > Tested on x86_64-linux. OK for trunk?
> > >
> > > Additionally, regarding the implementation strategy for other standard
> > > containers like std::list, it seems inevitable that we will need to
> > > move some function implementations (like _List_node_base) from the .cc
> > > files to the headers. This looks like an ABI break. Is this
> > > acceptable?
> >
> > No. It needs to be done without ABI breaks (which is possible, but
> > awkward and complex).
> >
>
> BTW, could you elaborate more on how to achieve this in general? The
> only approach I can think of is using attributes like noinline and
> default visibility.
Neither of those.
There are several ways. They only need to be constexpr (and therefore
inline) for C++26, so the definitions can be copied into the headers
like so:
#if __cplusplus >= 202603L
// inline definitions
#endif
and keep them in src/c++98/list.cc as well, where they are compiled
with -std=gnu++98. (The code duplication can be solved easily enough
by putting them in a dedicated header and appropriate use of macros.)
Programs which expect to find the definitions in the library will
still find them in the library, programs compiled with -std=gnu++26
will get the definitions in the header. We already do similar things
for std::string and everything in <stdexcept>.
On Tue, 2 Jun 2026 at 19:00, Jonathan Wakely <jwakely@redhat.com> wrote:
>
> On Tue, 2 Jun 2026 at 18:04, Yuao Ma <addr2line@gmail.com> wrote:
> >
> > On Tue, Jun 2, 2026 at 11:01 PM Jonathan Wakely <jwakely@redhat.com> wrote:
> > >
> > > On Tue, 2 Jun 2026 at 15:59, Yuao Ma <addr2line@gmail.com> wrote:
> > > >
> > > > Hi!
> > > >
> > > > This patch makes std::priority_queue constexpr as part of P3372R3. I
> > > > am unsure whether I should add the FTM here; since the constexpr
> > > > support for std::queue isn't complete yet, I have omitted the FTM from
> > > > this patch.
> > >
> > > Yes, that's the correct choice, thanks.
> > >
> > > > Tested on x86_64-linux. OK for trunk?
> > > >
> > > > Additionally, regarding the implementation strategy for other standard
> > > > containers like std::list, it seems inevitable that we will need to
> > > > move some function implementations (like _List_node_base) from the .cc
> > > > files to the headers. This looks like an ABI break. Is this
> > > > acceptable?
> > >
> > > No. It needs to be done without ABI breaks (which is possible, but
> > > awkward and complex).
> > >
> >
> > BTW, could you elaborate more on how to achieve this in general? The
> > only approach I can think of is using attributes like noinline and
> > default visibility.
>
> Neither of those.
>
> There are several ways. They only need to be constexpr (and therefore
> inline) for C++26, so the definitions can be copied into the headers
> like so:
>
> #if __cplusplus >= 202603L
> // inline definitions
> #endif
>
> and keep them in src/c++98/list.cc as well, where they are compiled
> with -std=gnu++98. (The code duplication can be solved easily enough
> by putting them in a dedicated header and appropriate use of macros.)
>
> Programs which expect to find the definitions in the library will
> still find them in the library, programs compiled with -std=gnu++26
> will get the definitions in the header. We already do similar things
> for std::string and everything in <stdexcept>.
See also how std::type_info::operator== is handled.
@@ -607,28 +607,35 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _Seq = _Sequence, typename _Requires = typename
enable_if<__and_<is_default_constructible<_Compare>,
is_default_constructible<_Seq>>::value>::type>
+ _GLIBCXX26_CONSTEXPR
priority_queue()
: c(), comp() { }
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Compare& __x, _Sequence&& __s = _Sequence())
: c(std::move(__s)), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
+ _GLIBCXX26_CONSTEXPR
priority_queue(const priority_queue&) = default;
+ _GLIBCXX26_CONSTEXPR
priority_queue& operator=(const priority_queue&) = default;
+ _GLIBCXX26_CONSTEXPR
priority_queue(priority_queue&& __q)
noexcept(__and_<is_nothrow_move_constructible<_Sequence>,
is_nothrow_move_constructible<_Compare>>::value)
: c(std::move(__q.c)), comp(std::move(__q.comp))
{ __q.c.clear(); }
+ _GLIBCXX26_CONSTEXPR
priority_queue&
operator=(priority_queue&& __q)
noexcept(__and_<is_nothrow_move_assignable<_Sequence>,
@@ -641,32 +648,38 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
explicit
priority_queue(const _Alloc& __a)
: c(__a), comp() { }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, const _Alloc& __a)
: c(__a), comp(__x) { }
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// 2537. Constructors [...] taking allocators should call make_heap
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, const _Sequence& __c,
const _Alloc& __a)
: c(__c, __a), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const _Compare& __x, _Sequence&& __c, const _Alloc& __a)
: c(std::move(__c), __a), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(const priority_queue& __q, const _Alloc& __a)
: c(__q.c, __a), comp(__q.comp) { }
template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(priority_queue&& __q, const _Alloc& __a)
: c(std::move(__q.c), __a), comp(std::move(__q.comp))
{ __q.c.clear(); }
@@ -704,6 +717,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 3529. priority_queue(first, last) should construct c with (first, last)
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x = _Compare())
: c(__first, __last), comp(__x)
@@ -713,6 +727,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// 3522. Missing requirement on InputIterator template parameter
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Sequence& __s)
: c(__s), comp(__x)
@@ -724,6 +739,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, _Sequence&& __s)
: c(std::move(__s)), comp(__x)
@@ -741,6 +757,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Alloc& __alloc)
: c(__first, __last, __alloc), comp()
@@ -749,6 +766,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Alloc& __alloc)
: c(__first, __last, __alloc), comp(__x)
@@ -757,6 +775,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename = std::_RequireInputIter<_InputIterator>,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, const _Sequence& __s,
const _Alloc& __alloc)
@@ -769,6 +788,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
template<typename _InputIterator, typename _Alloc,
typename _Requires = _Uses<_Alloc>>
+ _GLIBCXX26_CONSTEXPR
priority_queue(_InputIterator __first, _InputIterator __last,
const _Compare& __x, _Sequence&& __s,
const _Alloc& __alloc)
@@ -788,18 +808,21 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* @{
*/
template<__detail::__container_compatible_range<_Tp> _Rg>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg,
const _Compare& __x = _Compare())
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg))), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg, const _Compare& __x,
const _Alloc& __a)
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp(__x)
{ std::make_heap(c.begin(), c.end(), comp); }
template<__detail::__container_compatible_range<_Tp> _Rg, typename _Alloc>
+ _GLIBCXX26_CONSTEXPR
priority_queue(from_range_t, _Rg&& __rg, const _Alloc& __a)
: c(ranges::to<_Sequence>(std::forward<_Rg>(__rg), __a)), comp()
{ std::make_heap(c.begin(), c.end(), comp); }
@@ -809,12 +832,15 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
/**
* Returns true if the %queue is empty.
*/
- _GLIBCXX_NODISCARD bool
+ _GLIBCXX_NODISCARD
+ _GLIBCXX26_CONSTEXPR
+ bool
empty() const
{ return c.empty(); }
/** Returns the number of elements in the %queue. */
_GLIBCXX_NODISCARD
+ _GLIBCXX26_CONSTEXPR
size_type
size() const
{ return c.size(); }
@@ -824,6 +850,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* element of the %queue.
*/
_GLIBCXX_NODISCARD
+ _GLIBCXX26_CONSTEXPR
const_reference
top() const
{
@@ -839,6 +866,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* The time complexity of the operation depends on the underlying
* sequence.
*/
+ _GLIBCXX26_CONSTEXPR
void
push(const value_type& __x)
{
@@ -847,6 +875,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#if __cplusplus >= 201103L
+ _GLIBCXX26_CONSTEXPR
void
push(value_type&& __x)
{
@@ -855,6 +884,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
template<typename... _Args>
+ _GLIBCXX26_CONSTEXPR
void
emplace(_Args&&... __args)
{
@@ -865,6 +895,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __glibcxx_containers_ranges // C++ >= 23
template<__detail::__container_compatible_range<_Tp> _Rg>
+ _GLIBCXX26_CONSTEXPR
void
push_range(_Rg&& __rg)
{
@@ -887,6 +918,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
* data is needed, it should be retrieved before pop() is
* called.
*/
+ _GLIBCXX26_CONSTEXPR
void
pop()
{
@@ -896,6 +928,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
}
#if __cplusplus >= 201103L
+ _GLIBCXX26_CONSTEXPR
void
swap(priority_queue& __pq)
noexcept(__and_<
@@ -964,6 +997,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201103L
template<typename _Tp, typename _Sequence, typename _Compare>
+ _GLIBCXX26_CONSTEXPR
inline
#if __cplusplus > 201402L || !defined(__STRICT_ANSI__) // c++1z or gnu++11
// Constrained free swap overload, see p0185r1
new file mode 100644
@@ -0,0 +1,43 @@
+// { dg-do compile { target c++26 } }
+#include <queue>
+
+constexpr bool test_constexpr_priority_queue() {
+ std::priority_queue<int> pq;
+ if (!pq.empty()) return false;
+ if (pq.size() != 0) return false;
+
+ pq.push(10);
+ if (pq.empty()) return false;
+ if (pq.size() != 1) return false;
+ if (pq.top() != 10) return false;
+
+ pq.emplace(30);
+ pq.emplace(20);
+ if (pq.size() != 3) return false;
+ if (pq.top() != 30) return false;
+
+ pq.pop();
+ if (pq.size() != 2) return false;
+ if (pq.top() != 20) return false;
+
+ std::priority_queue<int> pq2;
+ pq2 = pq;
+ if (pq2.size() != 2) return false;
+ if (pq2.top() != 20) return false;
+
+ std::priority_queue<int> pq3;
+ pq3.push(100);
+ pq.swap(pq3);
+ if (pq.size() != 1 || pq.top() != 100) return false;
+ if (pq3.size() != 2 || pq3.top() != 20) return false;
+
+ int arr[] = {50, 200, 10};
+ pq.push_range(arr);
+ if (pq.size() != 4) return false;
+ if (pq.top() != 200) return false;
+
+ return true;
+}
+
+static_assert(test_constexpr_priority_queue(),
+ "constexpr priority_queue test failed!");