Skip to content

Commit 7c72199

Browse files
author
Xiaoyang Liu
authored
[libc++][ranges] LWG4016: container-insertable checks do not match what container-inserter does (#113103)
This patch implements LWG4016: container-insertable checks do not match what container-inserter does.
1 parent 5560f7e commit 7c72199

File tree

4 files changed

+90
-84
lines changed

4 files changed

+90
-84
lines changed

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@
4848
"`LWG4011 <https://wg21.link/LWG4011>`__","``""Effects: Equivalent to return""`` in ``[span.elem]``","2024-03 (Tokyo)","|Nothing To Do|","",""
4949
"`LWG4012 <https://wg21.link/LWG4012>`__","``common_view::begin/end`` are missing the ``simple-view`` check","2024-03 (Tokyo)","","",""
5050
"`LWG4013 <https://wg21.link/LWG4013>`__","``lazy_split_view::outer-iterator::value_type`` should not provide default constructor","2024-03 (Tokyo)","","",""
51-
"`LWG4016 <https://wg21.link/LWG4016>`__","container-insertable checks do not match what container-inserter does","2024-03 (Tokyo)","","",""
51+
"`LWG4016 <https://wg21.link/LWG4016>`__","container-insertable checks do not match what container-inserter does","2024-03 (Tokyo)","|Complete|","20.0",""
5252
"`LWG4023 <https://wg21.link/LWG4023>`__","Preconditions of ``std::basic_streambuf::setg/setp``","2024-03 (Tokyo)","|Complete|","19.0",""
5353
"`LWG4025 <https://wg21.link/LWG4025>`__","Move assignment operator of ``std::expected<cv void, E>`` should not be conditionally deleted","2024-03 (Tokyo)","|Complete|","20.0",""
5454
"`LWG4030 <https://wg21.link/LWG4030>`__","Clarify whether arithmetic expressions in ``[numeric.sat.func]`` are mathematical or C++","2024-03 (Tokyo)","|Nothing To Do|","",""

libcxx/include/__ranges/to.h

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,12 @@
1010
#ifndef _LIBCPP___RANGES_TO_H
1111
#define _LIBCPP___RANGES_TO_H
1212

13-
#include <__algorithm/ranges_copy.h>
1413
#include <__concepts/constructible.h>
1514
#include <__concepts/convertible_to.h>
1615
#include <__concepts/derived_from.h>
1716
#include <__concepts/same_as.h>
1817
#include <__config>
1918
#include <__functional/bind_back.h>
20-
#include <__iterator/back_insert_iterator.h>
21-
#include <__iterator/insert_iterator.h>
2219
#include <__iterator/iterator_traits.h>
2320
#include <__ranges/access.h>
2421
#include <__ranges/concepts.h>
@@ -54,21 +51,14 @@ constexpr bool __reservable_container =
5451
};
5552

5653
template <class _Container, class _Ref>
57-
constexpr bool __container_insertable = requires(_Container& __c, _Ref&& __ref) {
54+
constexpr bool __container_appendable = requires(_Container& __c, _Ref&& __ref) {
5855
requires(
56+
requires { __c.emplace_back(std::forward<_Ref>(__ref)); } ||
5957
requires { __c.push_back(std::forward<_Ref>(__ref)); } ||
58+
requires { __c.emplace(__c.end(), std::forward<_Ref>(__ref)); } ||
6059
requires { __c.insert(__c.end(), std::forward<_Ref>(__ref)); });
6160
};
6261

63-
template <class _Ref, class _Container>
64-
_LIBCPP_HIDE_FROM_ABI constexpr auto __container_inserter(_Container& __c) {
65-
if constexpr (requires { __c.push_back(std::declval<_Ref>()); }) {
66-
return std::back_inserter(__c);
67-
} else {
68-
return std::inserter(__c, __c.end());
69-
}
70-
}
71-
7262
// Note: making this a concept allows short-circuiting the second condition.
7363
template <class _Container, class _Range>
7464
concept __try_non_recursive_conversion =
@@ -113,14 +103,25 @@ template <class _Container, input_range _Range, class... _Args>
113103

114104
// Case 4 -- default-construct (or construct from the extra arguments) and insert, reserving the size if possible.
115105
else if constexpr (constructible_from<_Container, _Args...> &&
116-
__container_insertable<_Container, range_reference_t<_Range>>) {
106+
__container_appendable<_Container, range_reference_t<_Range>>) {
117107
_Container __result(std::forward<_Args>(__args)...);
118108
if constexpr (sized_range<_Range> && __reservable_container<_Container>) {
119109
__result.reserve(static_cast<range_size_t<_Container>>(ranges::size(__range)));
120110
}
121111

122-
ranges::copy(__range, ranges::__container_inserter<range_reference_t<_Range>>(__result));
123-
112+
for (auto&& __ref : __range) {
113+
using _Ref = decltype(__ref);
114+
if constexpr (requires { __result.emplace_back(declval<_Ref>()); }) {
115+
__result.emplace_back(std::forward<_Ref>(__ref));
116+
} else if constexpr (requires { __result.push_back(declval<_Ref>()); }) {
117+
__result.push_back(std::forward<_Ref>(__ref));
118+
} else if constexpr (requires { __result.emplace(__result.end(), declval<_Ref>()); }) {
119+
__result.emplace(__result.end(), std::forward<_Ref>(__ref));
120+
} else {
121+
static_assert(requires { __result.insert(__result.end(), declval<_Ref>()); });
122+
__result.insert(__result.end(), std::forward<_Ref>(__ref));
123+
}
124+
}
124125
return __result;
125126

126127
} else {

libcxx/test/std/ranges/range.utility/range.utility.conv/container.h

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,11 @@
1010
#define RANGES_RANGE_UTILITY_RANGE_UTILITY_CONV_CONTAINER_H
1111

1212
#include <algorithm>
13-
#include <concepts>
1413
#include <cstddef>
1514

1615
enum class CtrChoice { Invalid, DefaultCtrAndInsert, BeginEndPair, FromRangeT, DirectCtr };
1716

18-
enum class InserterChoice { Invalid, Insert, PushBack };
17+
enum class InserterChoice { Invalid, Insert, Emplace, PushBack, EmplaceBack };
1918

2019
// Allows checking that `ranges::to` correctly follows the order of priority of different constructors -- e.g., if
2120
// 3 constructors are available, the `from_range_t` constructor is chosen in favor of the constructor taking two
@@ -96,27 +95,50 @@ struct Container {
9695
constexpr ElementType* end() { return buffer_ + size_; }
9796
constexpr std::size_t size() const { return size_; }
9897

98+
template <class T>
99+
constexpr void emplace_back(T val)
100+
requires(Inserter >= InserterChoice::EmplaceBack)
101+
{
102+
inserter_choice = InserterChoice::EmplaceBack;
103+
__push_back_impl(val);
104+
}
105+
99106
template <class T>
100107
constexpr void push_back(T val)
101108
requires(Inserter >= InserterChoice::PushBack)
102109
{
103110
inserter_choice = InserterChoice::PushBack;
104-
buffer_[size_] = val;
111+
__push_back_impl(val);
112+
}
113+
114+
template <class T>
115+
constexpr void __push_back_impl(T val) {
116+
buffer_[size_] = val;
105117
++size_;
106118
}
107119

120+
template <class T>
121+
constexpr ElementType* emplace(ElementType* where, T val)
122+
requires(Inserter >= InserterChoice::Emplace)
123+
{
124+
inserter_choice = InserterChoice::Emplace;
125+
return __insert_impl(where, val);
126+
}
127+
108128
template <class T>
109129
constexpr ElementType* insert(ElementType* where, T val)
110130
requires(Inserter >= InserterChoice::Insert)
111131
{
112-
assert(size() + 1 <= Capacity);
113-
114132
inserter_choice = InserterChoice::Insert;
133+
return __insert_impl(where, val);
134+
}
115135

136+
template <class T>
137+
constexpr ElementType* __insert_impl(ElementType* where, T val) {
138+
assert(size() + 1 <= Capacity);
116139
std::shift_right(where, end(), 1);
117140
*where = val;
118141
++size_;
119-
120142
return where;
121143
}
122144

libcxx/test/std/ranges/range.utility/range.utility.conv/to.pass.cpp

Lines changed: 44 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -392,72 +392,55 @@ constexpr void test_ctr_choice_order() {
392392
}
393393

394394
{ // Case 4 -- default-construct then insert elements.
395-
{
396-
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
397-
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
398-
399-
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
400-
assert(result.inserter_choice == InserterChoice::Insert);
401-
assert(std::ranges::equal(result, in));
402-
assert(!result.called_reserve);
403-
assert((in | std::ranges::to<C>()) == result);
404-
auto closure = std::ranges::to<C>();
405-
assert((in | closure) == result);
406-
}
407-
408-
{
409-
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/true>;
410-
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
411-
412-
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
413-
assert(result.inserter_choice == InserterChoice::Insert);
414-
assert(std::ranges::equal(result, in));
415-
assert(result.called_reserve);
416-
assert((in | std::ranges::to<C>()) == result);
417-
auto closure = std::ranges::to<C>();
418-
assert((in | closure) == result);
419-
}
420-
421-
{
422-
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/false>;
423-
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
395+
auto case_4 = [in, arg1, arg2]<auto InserterChoice, bool CanReserve>() {
396+
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice, CanReserve>;
397+
{
398+
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
399+
400+
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
401+
assert(result.inserter_choice == InserterChoice);
402+
assert(std::ranges::equal(result, in));
403+
404+
if constexpr (CanReserve) {
405+
assert(result.called_reserve);
406+
} else {
407+
assert(!result.called_reserve);
408+
}
424409

425-
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
426-
assert(result.inserter_choice == InserterChoice::PushBack);
427-
assert(std::ranges::equal(result, in));
428-
assert(!result.called_reserve);
429-
assert((in | std::ranges::to<C>()) == result);
430-
auto closure = std::ranges::to<C>();
431-
assert((in | closure) == result);
432-
}
410+
assert((in | std::ranges::to<C>()) == result);
411+
[[maybe_unused]] auto closure = std::ranges::to<C>();
412+
assert((in | closure) == result);
413+
}
433414

434-
{
435-
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::PushBack, /*CanReserve=*/true>;
436-
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in);
415+
{ // Extra arguments
416+
[[maybe_unused]] std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
437417

438-
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
439-
assert(result.inserter_choice == InserterChoice::PushBack);
440-
assert(std::ranges::equal(result, in));
441-
assert(result.called_reserve);
442-
assert((in | std::ranges::to<C>()) == result);
443-
auto closure = std::ranges::to<C>();
444-
assert((in | closure) == result);
445-
}
418+
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
419+
assert(result.inserter_choice == InserterChoice);
420+
assert(std::ranges::equal(result, in));
421+
assert(result.extra_arg1 == arg1);
422+
assert(result.extra_arg2 == arg2);
446423

447-
{ // Extra arguments.
448-
using C = Container<int, CtrChoice::DefaultCtrAndInsert, InserterChoice::Insert, /*CanReserve=*/false>;
449-
std::same_as<C> decltype(auto) result = std::ranges::to<C>(in, arg1, arg2);
424+
if constexpr (CanReserve) {
425+
assert(result.called_reserve);
426+
} else {
427+
assert(!result.called_reserve);
428+
}
450429

451-
assert(result.ctr_choice == CtrChoice::DefaultCtrAndInsert);
452-
assert(result.inserter_choice == InserterChoice::Insert);
453-
assert(std::ranges::equal(result, in));
454-
assert(!result.called_reserve);
455-
assert(result.extra_arg1 == arg1);
456-
assert(result.extra_arg2 == arg2);
457-
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
458-
auto closure = std::ranges::to<C>(arg1, arg2);
459-
assert((in | closure) == result);
460-
}
430+
assert((in | std::ranges::to<C>(arg1, arg2)) == result);
431+
[[maybe_unused]] auto closure = std::ranges::to<C>(arg1, arg2);
432+
assert((in | closure) == result);
433+
}
434+
};
435+
436+
case_4.operator()<InserterChoice::Insert, false>();
437+
case_4.operator()<InserterChoice::Insert, true>();
438+
case_4.operator()<InserterChoice::Emplace, false>();
439+
case_4.operator()<InserterChoice::Emplace, true>();
440+
case_4.operator()<InserterChoice::PushBack, false>();
441+
case_4.operator()<InserterChoice::PushBack, true>();
442+
case_4.operator()<InserterChoice::EmplaceBack, false>();
443+
case_4.operator()<InserterChoice::EmplaceBack, true>();
461444
}
462445
}
463446

0 commit comments

Comments
 (0)