[libc++][ranges] Implement the changes to container adaptors from P1206 (ranges::to):

- add the `from_range_t` constructors and the related deduction guides;
- add the `push_range` member function.

(Note: this patch is split from https://reviews.llvm.org/D142335)

Reviewed By: #libc, ldionne

Differential Revision: https://reviews.llvm.org/D149829
This commit is contained in:
varconst
2023-06-02 19:23:29 -07:00
parent ab8d4f5a12
commit 87f3ff3e55
21 changed files with 1162 additions and 54 deletions

View File

@@ -43,6 +43,7 @@ public:
explicit queue(container_type&& c)
template<class InputIterator>
queue(InputIterator first, InputIterator last); // since C++23
template<container-compatible-range<T> R> queue(from_range_t, R&& rg); // since C++23
template <class Alloc>
explicit queue(const Alloc& a);
template <class Alloc>
@@ -55,6 +56,8 @@ public:
queue(queue&& q, const Alloc& a);
template <class InputIterator, class Alloc>
queue(InputIterator first, InputIterator last, const Alloc&); // since C++23
template<container-compatible-range<T> R, class Alloc>
queue(from_range_t, R&& rg, const Alloc&); // since C++23
bool empty() const;
size_type size() const;
@@ -66,6 +69,8 @@ public:
void push(const value_type& v);
void push(value_type&& v);
template<container-compatible-range<T> R>
void push_range(R&& rg); // C++23
template <class... Args> reference emplace(Args&&... args); // reference in C++17
void pop();
@@ -78,6 +83,9 @@ template<class Container>
template<class InputIterator>
queue(InputIterator, InputIterator) -> queue<iter-value-type<InputIterator>>; // since C++23
template<ranges::input_range R>
queue(from_range_t, R&&) -> queue<ranges::range_value_t<R>>; // since C++23
template<class Container, class Allocator>
queue(Container, Allocator) -> queue<typename Container::value_type, Container>; // C++17
@@ -86,6 +94,10 @@ template<class InputIterator, class Allocator>
-> queue<iter-value-type<InputIterator>,
deque<iter-value-type<InputIterator>, Allocator>>; // since C++23
template<ranges::input_range R, class Allocator>
queue(from_range_t, R&&, Allocator)
-> queue<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
template <class T, class Container>
bool operator==(const queue<T, Container>& x,const queue<T, Container>& y);
@@ -138,6 +150,8 @@ public:
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last,
const Compare& comp, Container&& c);
template <container-compatible-range<T> R>
priority_queue(from_range_t, R&& rg, const Compare& x = Compare()); // since C++23
template <class Alloc>
explicit priority_queue(const Alloc& a);
template <class Alloc>
@@ -160,6 +174,10 @@ public:
template <class InputIterator>
priority_queue(InputIterator first, InputIterator last,
const Compare& comp, Container&& c, const Alloc& a);
template <container-compatible-range<T> R, class Alloc>
priority_queue(from_range_t, R&& rg, const Compare&, const Alloc&); // since C++23
template <container-compatible-range<T> R, class Alloc>
priority_queue(from_range_t, R&& rg, const Alloc&); // since C++23
template <class Alloc>
priority_queue(const priority_queue& q, const Alloc& a);
template <class Alloc>
@@ -171,6 +189,8 @@ public:
void push(const value_type& v);
void push(value_type&& v);
template<container-compatible-range<T> R>
void push_range(R&& rg); // C++23
template <class... Args> void emplace(Args&&... args);
void pop();
@@ -189,6 +209,10 @@ template<class InputIterator,
priority_queue(InputIterator, InputIterator, Compare = Compare(), Container = Container())
-> priority_queue<iter-value-type<InputIterator>, Container, Compare>; // C++17
template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>>
priority_queue(from_range_t, R&&, Compare = Compare())
-> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>>, Compare>; // C++23
template<class Compare, class Container, class Allocator>
priority_queue(Compare, Container, Allocator)
-> priority_queue<typename Container::value_type, Container, Compare>; // C++17
@@ -208,6 +232,15 @@ template<class InputIterator, class Compare, class Container, class Allocator>
priority_queue(InputIterator, InputIterator, Compare, Container, Allocator)
-> priority_queue<typename Container::value_type, Container, Compare>; // C++17
template<ranges::input_range R, class Compare, class Allocator>
priority_queue(from_range_t, R&&, Compare, Allocator)
-> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>,
Compare>; // C++23
template<ranges::input_range R, class Allocator>
priority_queue(from_range_t, R&&, Allocator)
-> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>>; // C++23
template <class T, class Container, class Compare>
void swap(priority_queue<T, Container, Compare>& x,
priority_queue<T, Container, Compare>& y)
@@ -220,11 +253,17 @@ template <class T, class Container, class Compare>
#include <__algorithm/make_heap.h>
#include <__algorithm/pop_heap.h>
#include <__algorithm/push_heap.h>
#include <__algorithm/ranges_copy.h>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__functional/operations.h>
#include <__iterator/back_insert_iterator.h>
#include <__iterator/iterator_traits.h>
#include <__memory/uses_allocator.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
#include <__utility/forward.h>
#include <deque>
#include <vector>
@@ -283,12 +322,24 @@ public:
_LIBCPP_HIDE_FROM_ABI
queue(_InputIterator __first, _InputIterator __last) : c(__first, __last) {}
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
queue(from_range_t, _Range&& __range) : c(from_range, std::forward<_Range>(__range)) {}
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
queue(_InputIterator __first, _InputIterator __second, const _Alloc& __alloc) : c(__first, __second, __alloc) {}
template <_ContainerCompatibleRange<_Tp> _Range,
class _Alloc,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
queue(from_range_t, _Range&& __range, const _Alloc& __alloc)
: c(from_range, std::forward<_Range>(__range), __alloc) {}
#endif
_LIBCPP_INLINE_VISIBILITY
@@ -360,6 +411,21 @@ public:
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
void push_range(_Range&& __range) {
if constexpr (requires (container_type& __c) {
__c.append_range(std::forward<_Range>(__range));
}) {
c.append_range(std::forward<_Range>(__range));
} else {
ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
}
}
#endif
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
#if _LIBCPP_STD_VER >= 17
@@ -418,12 +484,22 @@ template <class _InputIterator,
queue(_InputIterator, _InputIterator)
-> queue<__iter_value_type<_InputIterator>>;
template <ranges::input_range _Range>
queue(from_range_t, _Range&&)
-> queue<ranges::range_value_t<_Range>>;
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
queue(_InputIterator, _InputIterator, _Alloc)
-> queue<__iter_value_type<_InputIterator>, deque<__iter_value_type<_InputIterator>, _Alloc>>;
template <ranges::input_range _Range,
class _Alloc,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
queue(from_range_t, _Range&&, _Alloc)
-> queue<ranges::range_value_t<_Range>, deque<ranges::range_value_t<_Range>, _Alloc>>;
#endif
template <class _Tp, class _Container>
@@ -557,6 +633,17 @@ public:
priority_queue(_InputIter __f, _InputIter __l,
const value_compare& __comp, container_type&& __c);
#endif // _LIBCPP_CXX03_LANG
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
priority_queue(from_range_t, _Range&& __range, const value_compare& __comp = value_compare())
: c(from_range, std::forward<_Range>(__range)),
comp(__comp) {
std::make_heap(c.begin(), c.end(), comp);
}
#endif
template <class _Alloc>
_LIBCPP_INLINE_VISIBILITY
explicit priority_queue(const _Alloc& __a,
@@ -611,6 +698,30 @@ public:
__enable_if_t<uses_allocator<container_type, _Alloc>::value>* = 0);
#endif // _LIBCPP_CXX03_LANG
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range,
class _Alloc,
class = enable_if_t<uses_allocator<_Container, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
priority_queue(from_range_t, _Range&& __range, const value_compare& __comp, const _Alloc& __a)
: c(from_range, std::forward<_Range>(__range), __a),
comp(__comp) {
std::make_heap(c.begin(), c.end(), comp);
}
template <_ContainerCompatibleRange<_Tp> _Range,
class _Alloc,
class = enable_if_t<uses_allocator<_Container, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
priority_queue(from_range_t, _Range&& __range, const _Alloc& __a)
: c(from_range, std::forward<_Range>(__range), __a),
comp() {
std::make_heap(c.begin(), c.end(), comp);
}
#endif
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
bool empty() const {return c.empty();}
_LIBCPP_INLINE_VISIBILITY
@@ -623,6 +734,23 @@ public:
#ifndef _LIBCPP_CXX03_LANG
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v);
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
void push_range(_Range&& __range) {
if constexpr (requires (container_type& __c) {
__c.append_range(std::forward<_Range>(__range));
}) {
c.append_range(std::forward<_Range>(__range));
} else {
ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
}
std::make_heap(c.begin(), c.end(), comp);
}
#endif
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
void emplace(_Args&&... __args);
@@ -695,6 +823,31 @@ priority_queue(_InputIterator, _InputIterator, _Compare, _Container, _Alloc)
-> priority_queue<typename _Container::value_type, _Container, _Compare>;
#endif
#if _LIBCPP_STD_VER >= 23
template <ranges::input_range _Range,
class _Compare = less<ranges::range_value_t<_Range>>,
class = enable_if_t<!__is_allocator<_Compare>::value>>
priority_queue(from_range_t, _Range&&, _Compare = _Compare())
-> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>>, _Compare>;
template <ranges::input_range _Range,
class _Compare,
class _Alloc,
class = enable_if_t<!__is_allocator<_Compare>::value>,
class = enable_if_t<__is_allocator<_Alloc>::value>>
priority_queue(from_range_t, _Range&&, _Compare, _Alloc)
-> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>, _Alloc>,
_Compare>;
template <ranges::input_range _Range,
class _Alloc,
class = enable_if_t<__is_allocator<_Alloc>::value>>
priority_queue(from_range_t, _Range&&, _Alloc)
-> priority_queue<ranges::range_value_t<_Range>, vector<ranges::range_value_t<_Range>, _Alloc>>;
#endif
template <class _Tp, class _Container, class _Compare>
inline
priority_queue<_Tp, _Container, _Compare>::priority_queue(const _Compare& __comp,

View File

@@ -42,6 +42,7 @@ public:
explicit stack(const container_type& c);
explicit stack(container_type&& c);
template <class InputIterator> stack(InputIterator first, InputIterator last); // since C++23
template<container-compatible-range<T> R> stack(from_range_t, R&& rg); // since C++23
template <class Alloc> explicit stack(const Alloc& a);
template <class Alloc> stack(const container_type& c, const Alloc& a);
template <class Alloc> stack(container_type&& c, const Alloc& a);
@@ -49,6 +50,8 @@ public:
template <class Alloc> stack(stack&& c, const Alloc& a);
template<class InputIterator, class Alloc>
stack(InputIterator first, InputIterator last, const Alloc&); // since C++23
template<container-compatible-range<T> R, class Alloc>
stack(from_range_t, R&& rg, const Alloc&); // since C++23
bool empty() const;
size_type size() const;
@@ -57,6 +60,8 @@ public:
void push(const value_type& x);
void push(value_type&& x);
template<container-compatible-range<T> R>
void push_range(R&& rg); // C++23
template <class... Args> reference emplace(Args&&... args); // reference in C++17
void pop();
@@ -69,6 +74,9 @@ template<class Container>
template<class InputIterator>
stack(InputIterator, InputIterator) -> stack<iter-value-type<InputIterator>>; // since C++23
template<ranges::input_range R>
stack(from_range_t, R&&) -> stack<ranges::range_value_t<R>>; // since C++23
template<class Container, class Allocator>
stack(Container, Allocator) -> stack<typename Container::value_type, Container>; // C++17
@@ -77,6 +85,10 @@ template<class InputIterator, class Allocator>
-> stack<iter-value-type<InputIterator>,
deque<iter-value-type<InputIterator>, Allocator>>; // since C++23
template<ranges::input_range R, class Allocator>
stack(from_range_t, R&&, Allocator)
-> stack<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
template <class T, class Container>
bool operator==(const stack<T, Container>& x, const stack<T, Container>& y);
template <class T, class Container>
@@ -98,10 +110,16 @@ template <class T, class Container>
*/
#include <__algorithm/ranges_copy.h>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__iterator/back_insert_iterator.h>
#include <__iterator/iterator_traits.h>
#include <__memory/uses_allocator.h>
#include <__ranges/access.h>
#include <__ranges/concepts.h>
#include <__ranges/container_compatible_range.h>
#include <__ranges/from_range.h>
#include <__type_traits/is_same.h>
#include <__utility/forward.h>
#include <deque>
@@ -210,12 +228,24 @@ public:
_LIBCPP_HIDE_FROM_ABI
stack(_InputIterator __first, _InputIterator __last) : c(__first, __last) {}
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
stack(from_range_t, _Range&& __range) : c(from_range, std::forward<_Range>(__range)) {}
template <class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
stack(_InputIterator __first, _InputIterator __last, const _Alloc& __alloc) : c(__first, __last, __alloc) {}
template <_ContainerCompatibleRange<_Tp> _Range,
class _Alloc,
class = __enable_if_t<uses_allocator<container_type, _Alloc>::value>>
_LIBCPP_HIDE_FROM_ABI
stack(from_range_t, _Range&& __range, const _Alloc& __alloc)
: c(from_range, std::forward<_Range>(__range), __alloc) {}
#endif
_LIBCPP_NODISCARD_AFTER_CXX17 _LIBCPP_INLINE_VISIBILITY
@@ -233,6 +263,20 @@ public:
_LIBCPP_INLINE_VISIBILITY
void push(value_type&& __v) {c.push_back(_VSTD::move(__v));}
#if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<_Tp> _Range>
_LIBCPP_HIDE_FROM_ABI
void push_range(_Range&& __range) {
if constexpr (requires (container_type& __c) {
__c.append_range(std::forward<_Range>(__range));
}) {
c.append_range(std::forward<_Range>(__range));
} else {
ranges::copy(std::forward<_Range>(__range), std::back_inserter(c));
}
}
#endif
template <class... _Args>
_LIBCPP_INLINE_VISIBILITY
#if _LIBCPP_STD_VER >= 17
@@ -290,12 +334,22 @@ template<class _InputIterator,
stack(_InputIterator, _InputIterator)
-> stack<__iter_value_type<_InputIterator>>;
template <ranges::input_range _Range>
stack(from_range_t, _Range&&) -> stack<ranges::range_value_t<_Range>>;
template<class _InputIterator,
class _Alloc,
class = __enable_if_t<__has_input_iterator_category<_InputIterator>::value>,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
stack(_InputIterator, _InputIterator, _Alloc)
-> stack<__iter_value_type<_InputIterator>, deque<__iter_value_type<_InputIterator>, _Alloc>>;
template <ranges::input_range _Range,
class _Alloc,
class = __enable_if_t<__is_allocator<_Alloc>::value>>
stack(from_range_t, _Range&&, _Alloc)
-> stack<ranges::range_value_t<_Range>, deque<ranges::range_value_t<_Range>, _Alloc>>;
#endif
template <class _Tp, class _Container>

View File

@@ -777,6 +777,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
stack limits
stack type_traits
stack version
stdexcept cstdlib
1 algorithm atomic
777 stack deque
778 stack functional
779 stack initializer_list
780 stack limits
781 stack type_traits
782 stack version
783 stdexcept cstdlib

View File

@@ -778,6 +778,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
stack limits
stack type_traits
stack version
stdexcept cstdlib
1 algorithm atomic
778 stack deque
779 stack functional
780 stack initializer_list
781 stack limits
782 stack type_traits
783 stack version
784 stdexcept cstdlib

View File

@@ -780,6 +780,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
stack limits
stack type_traits
stack version
stdexcept cstdlib
1 algorithm atomic
780 stack deque
781 stack functional
782 stack initializer_list
783 stack limits
784 stack type_traits
785 stack version
786 stdexcept cstdlib

View File

@@ -780,6 +780,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
stack limits
stack type_traits
stack version
stdexcept cstdlib
1 algorithm atomic
780 stack deque
781 stack functional
782 stack initializer_list
783 stack limits
784 stack type_traits
785 stack version
786 stdexcept cstdlib

View File

@@ -786,6 +786,7 @@ stack cstddef
stack deque
stack functional
stack initializer_list
stack limits
stack type_traits
stack version
stdexcept cstdlib
1 algorithm atomic
786 stack deque
787 stack functional
788 stack initializer_list
789 stack limits
790 stack type_traits
791 stack version
792 stdexcept cstdlib

View File

@@ -437,6 +437,7 @@ ostream typeinfo
ostream version
queue compare
queue cstddef
queue cstdint
queue deque
queue initializer_list
queue limits
@@ -529,8 +530,10 @@ sstream string
sstream version
stack compare
stack cstddef
stack cstdint
stack deque
stack initializer_list
stack limits
stack version
stdexcept iosfwd
stop_token atomic
1 algorithm climits
437 ostream version
438 queue compare
439 queue cstddef
440 queue cstdint
441 queue deque
442 queue initializer_list
443 queue limits
530 sstream version
531 stack compare
532 stack cstddef
533 stack cstdint
534 stack deque
535 stack initializer_list
536 stack limits
537 stack version
538 stdexcept iosfwd
539 stop_token atomic

View File

@@ -0,0 +1,229 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H
#define SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H
#include <algorithm>
#include <cassert>
#include <cstddef>
#include <queue>
#include <ranges>
#include <utility>
#include <vector>
#include "../from_range_helpers.h"
#include "MoveOnly.h"
#include "almost_satisfies_types.h"
#include "count_new.h"
#include "test_macros.h"
#include "unwrap_container_adaptor.h"
template <class Container, class Range>
concept HasFromRangeCtr = requires (Range&& range) {
Container(std::from_range, std::forward<Range>(range));
Container(std::from_range, std::forward<Range>(range), std::allocator<typename Container::value_type>());
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints() {
// Input range with the same value type.
static_assert(HasFromRangeCtr<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasFromRangeCtr<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasFromRangeCtr<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasFromRangeCtr<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
template <template <class ...> class Adaptor,
template <class ...> class UnderlyingContainer,
class T,
class Iter,
class Sent,
class Alloc>
constexpr void test_container_adaptor_with_input(std::vector<T>&& input) {
auto b = Iter(input.data());
auto e = Iter(input.data() + input.size());
std::ranges::subrange in(std::move(b), Sent(std::move(e)));
{ // (range)
Adaptor<T> adaptor(std::from_range, in);
UnwrapAdaptor<Adaptor<T>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::equal(in, c));
LIBCPP_ASSERT(c.__invariants());
}
{ // (range, allocator)
using C = UnderlyingContainer<T, Alloc>;
Alloc alloc;
Adaptor<T, C> adaptor(std::from_range, in, alloc);
UnwrapAdaptor<Adaptor<T, C>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.get_allocator() == alloc);
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::equal(in, c));
LIBCPP_ASSERT(c.__invariants());
}
}
template <template <class ...> class UnderlyingContainer,
class T,
class Iter,
class Sent,
class Comp,
class Alloc>
constexpr void test_priority_queue_with_input(std::vector<T>&& input) {
auto b = Iter(input.data());
auto e = Iter(input.data() + input.size());
std::ranges::subrange in(std::move(b), Sent(std::move(e)));
{ // (range)
std::priority_queue<T> adaptor(std::from_range, in);
UnwrapAdaptor<std::priority_queue<T>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::is_permutation(input, c));
LIBCPP_ASSERT(c.__invariants());
}
{ // (range, comp)
using C = UnderlyingContainer<T>;
Comp comp;
std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp);
UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::is_permutation(input, c));
LIBCPP_ASSERT(c.__invariants());
assert(unwrap_adaptor.get_comparator() == comp);
}
{ // (range, allocator)
using C = UnderlyingContainer<T, Alloc>;
Alloc alloc;
std::priority_queue<T, C> adaptor(std::from_range, in, alloc);
UnwrapAdaptor<std::priority_queue<T, C>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.get_allocator() == alloc);
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::is_permutation(input, c));
LIBCPP_ASSERT(c.__invariants());
}
{ // (range, comp, alloc)
using C = UnderlyingContainer<T, Alloc>;
Comp comp;
Alloc alloc;
std::priority_queue<T, C, Comp> adaptor(std::from_range, in, comp, alloc);
UnwrapAdaptor<std::priority_queue<T, C, Comp>> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.get_allocator() == alloc);
assert(c.size() == static_cast<std::size_t>(std::distance(c.begin(), c.end())));
assert(std::ranges::is_permutation(input, c));
LIBCPP_ASSERT(c.__invariants());
assert(unwrap_adaptor.get_comparator() == comp);
}
}
template <template <class ...> class Adaptor,
template <class ...> class UnderlyingContainer,
class T,
class Iter,
class Sent,
class Alloc>
constexpr void test_container_adaptor() {
auto test_with_input = &test_container_adaptor_with_input<Adaptor, UnderlyingContainer, T, Iter, Sent, Alloc>;
// Normal input.
test_with_input({0, 5, 12, 7, -1, 8, 26});
// Empty input.
test_with_input({});
// Single-element input.
test_with_input({5});
}
template <template <class ...> class UnderlyingContainer,
class T,
class Iter,
class Sent,
class Comp,
class Alloc>
constexpr void test_priority_queue() {
auto test_with_input = &test_priority_queue_with_input<UnderlyingContainer, T, Iter, Sent, Comp, Alloc>;
// Normal input.
test_with_input({0, 5, 12, 7, -1, 8, 26});
// Empty input.
test_with_input({});
// Single-element input.
test_with_input({5});
}
template <template <class ...> class Container>
constexpr void test_container_adaptor_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
[[maybe_unused]] Container<MoveOnly> c(std::from_range, in);
}
template <template <class ...> class Adaptor>
void test_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
using T = ThrowingCopy<3>;
T::reset();
T in[5];
try {
Adaptor<T, std::vector<T>> c(std::from_range, in);
assert(false); // The constructor call above should throw.
} catch (int) {
assert(T::created_by_copying == 3);
assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
}
#endif
}
template <template <class ...> class Adaptor, class T>
void test_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
T in[] = {0, 1};
try {
using C = std::vector<T, ThrowingAllocator<T>>;
ThrowingAllocator<T> alloc;
globalMemCounter.reset();
Adaptor<T, C> c(std::from_range, in, alloc);
assert(false); // The constructor call should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
#endif // SUPPORT_FROM_RANGE_CONTAINER_ADAPTORS_H

View File

@@ -22,14 +22,26 @@
// template<class Compare, class Container, class Allocator>
// priority_queue(Compare, Container, Allocator)
// -> priority_queue<typename Container::value_type, Container, Compare>;
//
// template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>>
// priority_queue(from_range_t, R&&, Compare = Compare())
// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>>, Compare>; // C++23
//
// template<ranges::input_range R, class Compare, class Allocator>
// priority_queue(from_range_t, R&&, Compare, Allocator)
// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>,
// Compare>; // C++23
//
// template<ranges::input_range R, class Allocator>
// priority_queue(from_range_t, R&&, Allocator)
// -> priority_queue<ranges::range_value_t<R>, vector<ranges::range_value_t<R>, Allocator>>; // C++23
#include <cassert>
#include <climits> // INT_MAX
#include <cstddef>
#include <iterator>
#include <queue>
#include <vector>
#include <iterator>
#include <cassert>
#include <cstddef>
#include <climits> // INT_MAX
#include "deduction_guides_sfinae_checks.h"
#include "test_macros.h"
@@ -238,6 +250,28 @@ int main(int, char**)
std::priority_queue pri(a, a+2, Comp(), std::move(cont), ConvertibleToAlloc(2));
static_assert(std::is_same_v<decltype(pri), std::priority_queue<T, Cont, Comp>>);
}
#if TEST_STD_VER >= 23
{ // (from_range, range)
std::priority_queue c(std::from_range, Cont());
static_assert(std::is_same_v<decltype(c), std::priority_queue<T>>);
}
{ // (from_range, range, compare)
std::priority_queue c(std::from_range, Cont(), Comp());
static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T>, Comp>>);
}
{ // (from_range, range, compare, alloc)
std::priority_queue c(std::from_range, Cont(), Comp(), Alloc(2));
static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T, Alloc>, Comp>>);
}
{ // (from_range, range, alloc)
std::priority_queue c(std::from_range, Cont(), Alloc(2));
static_assert(std::is_same_v<decltype(c), std::priority_queue<T, std::vector<T, Alloc>>>);
}
#endif // TEST_STD_VER >= 23
}
// Deduction guides should be SFINAE'd away when given:
@@ -354,6 +388,39 @@ int main(int, char**)
static_assert(SFINAEs_away<std::priority_queue, AllocAsComp, Cont>);
// Cannot deduce from (comp, ALLOC_as_cont)
static_assert(SFINAEs_away<std::priority_queue, Comp, AllocAsCont>);
#if TEST_STD_VER >= 23
using Range = RangeT<int>;
using BadRange = BadRangeT<int>;
// (from_range, range)
//
// Cannot deduce from (from_range, BAD_range)
static_assert(SFINAEs_away<std::priority_queue, BadRange>);
// (from_range, range, compare)
//
// Cannot deduce from (from_range, BAD_range, compare)
static_assert(SFINAEs_away<std::priority_queue, BadRange, Comp>);
// (from_range, range, compare, alloc)
//
// Cannot deduce from (from_range, BAD_range, compare, alloc)
static_assert(SFINAEs_away<std::priority_queue, BadRange, Comp, Alloc>);
// Cannot deduce from (from_range, range, compare, BAD_alloc)
static_assert(SFINAEs_away<std::priority_queue, Range, Comp, BadAlloc>);
// Cannot deduce from (from_range, range, compare, DIFFERENT_alloc)
static_assert(SFINAEs_away<std::priority_queue, Range, Comp, DiffAlloc>);
// (from_range, range, alloc)
//
// Cannot deduce from (from_range, BAD_range, alloc)
static_assert(SFINAEs_away<std::priority_queue, BadRange, Alloc>);
// Cannot deduce from (from_range, range, BAD_alloc)
static_assert(SFINAEs_away<std::priority_queue, Range, BadAlloc>);
// Cannot deduce from (from_range, range, DIFFERENT_alloc)
static_assert(SFINAEs_away<std::priority_queue, Range, DiffAlloc>);
#endif // TEST_STD_VER >= 23
}
return 0;

View File

@@ -0,0 +1,59 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
#include <queue>
#include "../../from_range_container_adaptors.h"
#include "../../../test_compare.h"
#include "test_macros.h"
// template <container-compatible-range<T> R>
// priority_queue(from_range_t, R&& rg, const Compare& x = Compare()); // since C++23
// template <container-compatible-range<T> R, class Alloc>
// priority_queue(from_range_t, R&& rg, const Compare&, const Alloc&); // since C++23
// template <container-compatible-range<T> R, class Alloc>
// priority_queue(from_range_t, R&& rg, const Alloc&); // since C++23
template <class Range>
concept PriorityQueueHasFromRangeCtr = requires (Range&& range) {
std::priority_queue<int>(std::from_range, std::forward<Range>(range));
std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::less<int>());
std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::less<int>(), std::allocator<int>());
std::priority_queue<int>(std::from_range, std::forward<Range>(range), std::allocator<int>());
};
constexpr bool test_constraints_priority_queue() {
// Input range with the same value type.
static_assert(PriorityQueueHasFromRangeCtr<InputRange<int>>);
// Input range with a convertible value type.
static_assert(PriorityQueueHasFromRangeCtr<InputRange<double>>);
// Input range with a non-convertible value type.
static_assert(!PriorityQueueHasFromRangeCtr<InputRange<Empty>>);
// Not an input range.
static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotDerivedFrom>);
static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotIndirectlyReadable>);
static_assert(!PriorityQueueHasFromRangeCtr<InputRangeNotInputOrOutputIterator>);
return true;
}
int main(int, char**) {
for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
test_priority_queue<std::vector, int, Iter, Sent, test_less<int>, Alloc>();
});
test_container_adaptor_move_only<std::priority_queue>();
static_assert(test_constraints_priority_queue());
test_exception_safety_throwing_copy<std::priority_queue>();
test_exception_safety_throwing_allocator<std::priority_queue, int>();
return 0;
}

View File

@@ -0,0 +1,33 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// template<container-compatible-range<T> R>
// void push_range(R&& rg); // C++23
#include <queue>
#include "../../push_range_container_adaptors.h"
#include "test_macros.h"
int main(int, char**) {
for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
test_push_range<std::priority_queue<int, std::vector<int, Alloc>>, Iter, Sent>(/*is_result_heapified=*/true);
});
test_push_range_move_only<std::priority_queue>();
test_push_range_inserter_choice<std::priority_queue, int>(/*is_result_heapified=*/true);
static_assert(test_constraints_push_range<std::priority_queue, int, double>());
test_push_range_exception_safety_throwing_copy<std::priority_queue>();
test_push_range_exception_safety_throwing_allocator<std::priority_queue, std::vector, int>();
return 0;
}

View File

@@ -0,0 +1,299 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H
#define SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H
#include <algorithm>
#include <cassert>
#include <concepts>
#include <cstddef>
#include <initializer_list>
#include <ranges>
#include <type_traits>
#include <vector>
#include "../from_range_helpers.h"
#include "../insert_range_helpers.h"
#include "MoveOnly.h"
#include "almost_satisfies_types.h"
#include "count_new.h"
#include "min_allocator.h"
#include "test_allocator.h"
#include "test_iterators.h"
#include "test_macros.h"
#include "type_algorithms.h"
#include "unwrap_container_adaptor.h"
template <class Container, class Range>
concept HasPushRange = requires (Container& c, Range&& range) {
c.push_range(range);
};
template <template <class...> class Container, class T, class U>
constexpr bool test_constraints_push_range() {
// Input range with the same value type.
static_assert(HasPushRange<Container<T>, InputRange<T>>);
// Input range with a convertible value type.
static_assert(HasPushRange<Container<T>, InputRange<U>>);
// Input range with a non-convertible value type.
static_assert(!HasPushRange<Container<T>, InputRange<Empty>>);
// Not an input range.
static_assert(!HasPushRange<Container<T>, InputRangeNotDerivedFrom>);
static_assert(!HasPushRange<Container<T>, InputRangeNotIndirectlyReadable>);
static_assert(!HasPushRange<Container<T>, InputRangeNotInputOrOutputIterator>);
return true;
}
// Empty container.
template <class T>
TestCase<T> constexpr EmptyContainer_EmptyRange {
.initial = {}, .input = {}, .expected = {}
};
template <class T> constexpr TestCase<T> EmptyContainer_OneElementRange {
.initial = {}, .input = {5}, .expected = {5}
};
template <class T> constexpr TestCase<T> EmptyContainer_MidRange {
.initial = {}, .input = {5, 3, 1, 7, 9}, .expected = {5, 3, 1, 7, 9}
};
// One-element container.
template <class T> constexpr TestCase<T> OneElementContainer_EmptyRange {
.initial = {3}, .input = {}, .expected = {3}
};
template <class T> constexpr TestCase<T> OneElementContainer_OneElementRange {
.initial = {3}, .input = {-5}, .expected = {3, -5}
};
template <class T> constexpr TestCase<T> OneElementContainer_MidRange {
.initial = {3}, .input = {-5, -3, -1, -7, -9}, .expected = {3, -5, -3, -1, -7, -9}
};
// Full container.
template <class T> constexpr TestCase<T> FullContainer_EmptyRange {
.initial = {11, 29, 35, 14, 84}, .input = {}, .expected = {11, 29, 35, 14, 84}
};
template <class T> constexpr TestCase<T> FullContainer_OneElementRange {
.initial = {11, 29, 35, 14, 84}, .input = {-5}, .expected = {11, 29, 35, 14, 84, -5}
};
template <class T> constexpr TestCase<T> FullContainer_MidRange {
.initial = {11, 29, 35, 14, 84},
.input = {-5, -3, -1, -7, -9},
.expected = {11, 29, 35, 14, 84, -5, -3, -1, -7, -9}
};
template <class T> constexpr TestCase<T> FullContainer_LongRange {
.initial = {11, 29, 35, 14, 84},
.input = {-5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48},
.expected = {
11, 29, 35, 14, 84, -5, -3, -1, -7, -9, -19, -48, -56, -13, -14, -29, -88, -17, -1, -5, -11, -89, -21, -33, -48
}
};
// Container adaptors tests.
template <class Adaptor, class Iter, class Sent>
constexpr void test_push_range(bool is_result_heapified = false) {
using T = typename Adaptor::value_type;
auto test = [&](auto& test_case) {
Adaptor adaptor(test_case.initial.begin(), test_case.initial.end());
auto in = wrap_input<Iter, Sent>(test_case.input);
adaptor.push_range(in);
UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
if (is_result_heapified) {
assert(std::ranges::is_heap(c));
return std::ranges::is_permutation(c, test_case.expected);
} else {
return std::ranges::equal(c, test_case.expected);
}
};
{ // Empty container.
// empty_c.push_range(empty_range)
assert(test(EmptyContainer_EmptyRange<T>));
// empty_c.push_range(one_element_range)
assert(test(EmptyContainer_OneElementRange<T>));
// empty_c.push_range(mid_range)
assert(test(EmptyContainer_MidRange<T>));
}
{ // One-element container.
// one_element_c.push_range(empty_range)
assert(test(OneElementContainer_EmptyRange<T>));
// one_element_c.push_range(one_element_range)
assert(test(OneElementContainer_OneElementRange<T>));
// one_element_c.push_range(mid_range)
assert(test(OneElementContainer_MidRange<T>));
}
{ // Full container.
// full_container.push_range(empty_range)
assert(test(FullContainer_EmptyRange<T>));
// full_container.push_range(one_element_range)
assert(test(FullContainer_OneElementRange<T>));
// full_container.push_range(mid_range)
assert(test(FullContainer_MidRange<T>));
// full_container.push_range(long_range)
assert(test(FullContainer_LongRange<T>));
}
}
// Move-only types.
template <template <class ...> class Container>
constexpr void test_push_range_move_only() {
MoveOnly input[5];
std::ranges::subrange in(std::move_iterator{input}, std::move_iterator{input + 5});
Container<MoveOnly> c;
c.push_range(in);
}
// Check that `append_range` is preferred if available and `push_back` is used as a fallback.
enum class InserterChoice {
Invalid,
PushBack,
AppendRange
};
template <class T, InserterChoice Inserter>
struct Container {
InserterChoice inserter_choice = InserterChoice::Invalid;
using value_type = T;
using iterator = T*;
using reference = T&;
using const_reference = const T&;
using size_type = std::size_t;
static constexpr int Capacity = 8;
int size_ = 0;
value_type buffer_[Capacity] = {};
iterator begin() { return buffer_; }
iterator end() { return buffer_ + size_; }
size_type size() const { return size_; }
template <class U>
void push_back(U val)
requires (Inserter >= InserterChoice::PushBack) {
inserter_choice = InserterChoice::PushBack;
buffer_[size_] = val;
++size_;
}
template <std::ranges::input_range Range>
void append_range(Range&& range)
requires (Inserter >= InserterChoice::AppendRange) {
assert(size() + std::ranges::distance(range) <= Capacity);
inserter_choice = InserterChoice::AppendRange;
for (auto&& e : range) {
buffer_[size_] = e;
++size_;
}
}
friend bool operator==(const Container&, const Container&) = default;
};
template <template <class ...> class AdaptorT, class T>
void test_push_range_inserter_choice(bool is_result_heapified = false) {
{ // `append_range` is preferred if available.
using BaseContainer = Container<T, InserterChoice::AppendRange>;
using Adaptor = AdaptorT<T, BaseContainer>;
T in[] = {1, 2, 3, 4, 5};
Adaptor adaptor;
adaptor.push_range(in);
UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.inserter_choice == InserterChoice::AppendRange);
if (is_result_heapified) {
assert(std::ranges::is_heap(c));
assert(std::ranges::is_permutation(c, in));
} else {
assert(std::ranges::equal(c, in));
}
}
{ // `push_back` is used as a fallback (via `back_inserter`).
using BaseContainer = Container<T, InserterChoice::PushBack>;
using Adaptor = AdaptorT<T, BaseContainer>;
T in[] = {1, 2, 3, 4, 5};
Adaptor adaptor;
adaptor.push_range(in);
UnwrapAdaptor<Adaptor> unwrap_adaptor(std::move(adaptor));
auto& c = unwrap_adaptor.get_container();
assert(c.inserter_choice == InserterChoice::PushBack);
if (is_result_heapified) {
assert(std::ranges::is_heap(c));
assert(std::ranges::is_permutation(c, in));
} else {
assert(std::ranges::equal(c, in));
}
}
}
// Exception safety.
template <template <class ...> class Container>
void test_push_range_exception_safety_throwing_copy() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
using T = ThrowingCopy<3>;
T::reset();
T in[5];
try {
Container<T> c;
c.push_range(in);
assert(false); // The function call above should throw.
} catch (int) {
assert(T::created_by_copying == 3);
assert(T::destroyed == 2); // No destructor call for the partially-constructed element.
}
#endif
}
template <template <class ...> class Adaptor, template <class ...> class BaseContainer, class T>
void test_push_range_exception_safety_throwing_allocator() {
#if !defined(TEST_HAS_NO_EXCEPTIONS)
T in[] = {0, 1};
try {
globalMemCounter.reset();
Adaptor<T, BaseContainer<T, ThrowingAllocator<T>>> c;
c.push_range(in);
assert(false); // The function call above should throw.
} catch (int) {
assert(globalMemCounter.new_called == globalMemCounter.delete_called);
}
#endif
}
#endif // SUPPORT_PUSH_RANGE_CONTAINER_ADAPTORS_H

View File

@@ -14,7 +14,13 @@
//
// template<class Container, class Allocator>
// queue(Container, Allocator) -> queue<typename Container::value_type, Container>;
//
// template<ranges::input_range R>
// queue(from_range_t, R&&) -> queue<ranges::range_value_t<R>>; // since C++23
//
// template<ranges::input_range R, class Allocator>
// queue(from_range_t, R&&, Allocator)
// -> queue<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
#include <array>
#include <queue>
@@ -134,29 +140,7 @@ int main(int, char**)
}
}
// Deduction guides should be SFINAE'd away when given:
// - a "bad" allocator (that is, a type not qualifying as an allocator);
// - an allocator instead of a container;
// - an allocator and a container that uses a different allocator.
{
using Cont = std::list<int>;
using Alloc = std::allocator<int>;
using DiffAlloc = test_allocator<int>;
using Iter = int*;
struct NotIter{};
struct NotAlloc {};
static_assert(SFINAEs_away<std::queue, Alloc, NotAlloc>);
static_assert(SFINAEs_away<std::queue, Cont, NotAlloc>);
static_assert(SFINAEs_away<std::queue, Cont, DiffAlloc>);
static_assert(SFINAEs_away<std::queue, Iter, NotIter>);
#if TEST_STD_VER > 20
static_assert(SFINAEs_away<std::queue, Iter, NotIter, Alloc>);
static_assert(SFINAEs_away<std::queue, Iter, Iter, NotAlloc>);
#endif
}
#if TEST_STD_VER > 20
#if TEST_STD_VER >= 23
{
typedef short T;
typedef test_allocator<T> Alloc;
@@ -170,6 +154,22 @@ int main(int, char**)
static_assert(std::is_same_v<decltype(q), std::queue<T, std::deque<T, Alloc>>>);
}
}
{
{
std::queue c(std::from_range, std::array<int, 0>());
static_assert(std::is_same_v<decltype(c), std::queue<int>>);
}
{
using Alloc = test_allocator<int>;
std::queue c(std::from_range, std::array<int, 0>(), Alloc());
static_assert(std::is_same_v<decltype(c), std::queue<int, std::deque<int, Alloc>>>);
}
}
#endif
ContainerAdaptorDeductionGuidesSfinaeAway<std::queue, std::queue<int>>();
return 0;
}

View File

@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
#include <queue>
#include "../../from_range_container_adaptors.h"
#include "test_macros.h"
// template<container-compatible-range<T> R> queue(from_range_t, R&& rg); // since C++23
// template<container-compatible-range<T> R, class Alloc>
// queue(from_range_t, R&& rg, const Alloc&); // since C++23
int main(int, char**) {
for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
test_container_adaptor<std::queue, std::deque, int, Iter, Sent, Alloc>();
});
test_container_adaptor_move_only<std::queue>();
static_assert(test_constraints<std::queue, int, double>());
test_exception_safety_throwing_copy<std::queue>();
test_exception_safety_throwing_allocator<std::queue, int>();
return 0;
}

View File

@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// template<container-compatible-range<T> R>
// void push_range(R&& rg); // C++23
#include <queue>
#include "../../push_range_container_adaptors.h"
#include "test_macros.h"
int main(int, char**) {
for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
test_push_range<std::queue<int, std::deque<int, Alloc>>, Iter, Sent>();
});
test_push_range_move_only<std::queue>();
test_push_range_inserter_choice<std::queue, int>();
static_assert(test_constraints_push_range<std::queue, int, double>());
test_push_range_exception_safety_throwing_copy<std::queue>();
test_push_range_exception_safety_throwing_allocator<std::queue, std::deque, int>();
return 0;
}

View File

@@ -14,7 +14,13 @@
//
// template<class Container, class Allocator>
// stack(Container, Allocator) -> stack<typename Container::value_type, Container>;
//
// template<ranges::input_range R>
// stack(from_range_t, R&&) -> stack<ranges::range_value_t<R>>; // since C++23
//
// template<ranges::input_range R, class Allocator>
// stack(from_range_t, R&&, Allocator)
// -> stack<ranges::range_value_t<R>, deque<ranges::range_value_t<R>, Allocator>>; // since C++23
#include <array>
#include <stack>
@@ -138,30 +144,7 @@ int main(int, char**)
}
}
// Deduction guides should be SFINAE'd away when given:
// - a "bad" allocator (that is, a type not qualifying as an allocator);
// - an allocator instead of a container;
// - an allocator and a container that uses a different allocator.
{
using Cont = std::list<int>;
using Alloc = std::allocator<int>;
using DiffAlloc = test_allocator<int>;
using Iter = int;
struct NotIter {};
struct NotAlloc {};
static_assert(SFINAEs_away<std::stack, Alloc, Alloc>);
static_assert(SFINAEs_away<std::stack, Cont, NotAlloc>);
static_assert(SFINAEs_away<std::stack, Cont, DiffAlloc>);
static_assert(SFINAEs_away<std::stack, Iter, NotIter>);
#if TEST_STD_VER > 20
static_assert(SFINAEs_away<std::stack, Iter, NotIter, Alloc>);
static_assert(SFINAEs_away<std::stack, Iter, Iter, NotAlloc>);
#endif
}
#if TEST_STD_VER > 20
#if TEST_STD_VER >= 23
{
typedef short T;
typedef test_allocator<T> Alloc;
@@ -175,7 +158,22 @@ int main(int, char**)
static_assert(std::is_same_v<decltype(s), std::stack<T, std::deque<T, Alloc>>>);
}
}
{
{
std::stack c(std::from_range, std::array<int, 0>());
static_assert(std::is_same_v<decltype(c), std::stack<int>>);
}
{
using Alloc = test_allocator<int>;
std::stack c(std::from_range, std::array<int, 0>(), Alloc());
static_assert(std::is_same_v<decltype(c), std::stack<int, std::deque<int, Alloc>>>);
}
}
#endif
ContainerAdaptorDeductionGuidesSfinaeAway<std::stack, std::stack<int>>();
return 0;
}

View File

@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
#include <stack>
#include "../../from_range_container_adaptors.h"
#include "test_macros.h"
// template<container-compatible-range<T> R> stack(from_range_t, R&& rg); // since C++23
// template<container-compatible-range<T> R, class Alloc>
// stack(from_range_t, R&& rg, const Alloc&); // since C++23
int main(int, char**) {
for_all_iterators_and_allocators<int>([]<class Iter, class Sent, class Alloc>() {
test_container_adaptor<std::stack, std::deque, int, Iter, Sent, Alloc>();
});
test_container_adaptor_move_only<std::stack>();
static_assert(test_constraints<std::stack, int, double>());
test_exception_safety_throwing_copy<std::stack>();
test_exception_safety_throwing_allocator<std::stack, int>();
return 0;
}

View File

@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// template<container-compatible-range<T> R>
// void push_range(R&& rg); // C++23
#include <stack>
#include "../../push_range_container_adaptors.h"
#include "test_macros.h"
int main(int, char**) {
for_all_iterators_and_allocators<int, const int*>([]<class Iter, class Sent, class Alloc>() {
test_push_range<std::stack<int, std::deque<int, Alloc>>, Iter, Sent>();
});
test_push_range_move_only<std::stack>();
test_push_range_inserter_choice<std::stack, int>();
static_assert(test_constraints_push_range<std::stack, int, double>());
test_push_range_exception_safety_throwing_copy<std::stack>();
test_push_range_exception_safety_throwing_allocator<std::stack, std::deque, int>();
return 0;
}

View File

@@ -108,6 +108,54 @@ constexpr void SequenceContainerDeductionGuidesSfinaeAway() {
#endif
}
// Deduction guides should be SFINAE'd away when given:
// - a "bad" allocator (that is, a type not qualifying as an allocator);
// - an allocator instead of a container;
// - an allocator and a container that uses a different allocator;
// - a range not satisfying the `input_range` concept.
template<template<typename ...> class Container, typename InstantiatedContainer>
constexpr void ContainerAdaptorDeductionGuidesSfinaeAway() {
using T = typename InstantiatedContainer::value_type;
using Alloc = std::allocator<T>;
using Iter = T*;
using BadIter = int;
using BadAlloc = Empty;
// (container) -- no constraints.
// (container, alloc)
//
// Cannot deduce from (container, BAD_alloc)
static_assert(SFINAEs_away<Container, std::vector<T>, BadAlloc>);
// (iter, iter)
//
// Cannot deduce from (BAD_iter, BAD_iter)
LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, BadIter, BadIter>);
#if TEST_STD_VER >= 23
using BadRange = BadRangeT<T>;
// (iter, iter, alloc)
//
// Cannot deduce from (BAD_iter, BAD_iter, alloc)
LIBCPP_STATIC_ASSERT(SFINAEs_away<Container, BadIter, BadIter, Alloc>);
// Cannot deduce from (iter, iter, BAD_alloc)
static_assert(SFINAEs_away<Container, Iter, Iter, BadAlloc>);
// (from_range, range)
//
// Cannot deduce from (BAD_range)
static_assert(SFINAEs_away<Container, std::from_range_t, BadRange>);
// (from_range, range, alloc)
//
// Cannot deduce from (range, BAD_alloc)
static_assert(SFINAEs_away<Container, std::from_range_t, RangeT<int>, BadAlloc>);
#endif
}
// For associative containers the deduction guides should be SFINAE'd away when
// given:
// - "bad" input iterators (that is, a type not qualifying as an input

View File

@@ -0,0 +1,32 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H
#define SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H
// Allows accessing the underlying container of the given adaptor.
template <class Adaptor>
struct UnwrapAdaptor : Adaptor {
UnwrapAdaptor() = default;
UnwrapAdaptor(Adaptor&& adaptor) : Adaptor(std::move(adaptor)) {}
// `c` is a protected member variable of the base class.
decltype(auto) get_container() {
return (UnwrapAdaptor::c); // Put into parentheses to make sure the function returns a reference.
}
// TODO: make this work pre-C++20.
decltype(auto) get_comparator()
requires requires {
UnwrapAdaptor::c;
} {
return (UnwrapAdaptor::comp); // Put into parentheses to make sure the function returns a reference.
}
};
#endif // SUPPORT_UNWRAP_CONTAINER_ADAPTOR_H