Skip to content

Commit 85167fb

Browse files
author
Arthur O'Dwyer
committed
[libc++] Further improve the contiguous-iterator story, and fix some bugs.
- Quality-of-implementation: Avoid calling __unwrap_iter in constexpr contexts. The user might conceivably write a contiguous iterator where normal iterator arithmetic is constexpr-friendly but `std::to_address(it)` isn't. - Bugfix: When you pass contiguous iterators to `std::copy`, you should get back your contiguous iterator type, not a raw pointer. That means that libc++ can't `__unwrap_iter` unless it also does `__rewrap_iter`. Fortunately, this is implementable. - Improve test coverage of the new `contiguous_iterator` test iterator. This catches the bug described above. - Tests: Stop testing that we can `std::copy` //into// an `input_iterator`. Our test iterators may currently support that, but it seems nonsensical to me. Differential Revision: https://reviews.llvm.org/D95983
1 parent b008ea3 commit 85167fb

File tree

8 files changed

+172
-40
lines changed

8 files changed

+172
-40
lines changed

libcxx/include/algorithm

Lines changed: 53 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1639,12 +1639,22 @@ search_n(_ForwardIterator __first, _ForwardIterator __last, _Size __count, const
16391639
__value_, __equal_to<__v, _Tp>());
16401640
}
16411641

1642-
// __unwrap_iter
1642+
// __unwrap_iter, __rewrap_iter
16431643

1644-
// The job of __unwrap_iter is to lower iterators-that-are-tantamount-to-pointers
1645-
// (such as vector<T>::iterator) into pointers, to reduce the number of template
1644+
// The job of __unwrap_iter is to lower contiguous iterators (such as
1645+
// vector<T>::iterator) into pointers, to reduce the number of template
16461646
// instantiations and to enable pointer-based optimizations e.g. in std::copy.
1647+
// For iterators that are not contiguous, it must be a no-op.
16471648
// In debug mode, we don't do this.
1649+
//
1650+
// __unwrap_iter is non-constexpr for user-defined iterators whose
1651+
// `to_address` and/or `operator->` is non-constexpr. This is okay; but we
1652+
// try to avoid doing __unwrap_iter in constant-evaluated contexts anyway.
1653+
//
1654+
// Some algorithms (e.g. std::copy, but not std::sort) need to convert an
1655+
// "unwrapped" result back into a contiguous iterator. Since contiguous iterators
1656+
// are random-access, we can do this portably using iterator arithmetic; this
1657+
// is the job of __rewrap_iter.
16481658

16491659
template <class _Iter, bool = __is_cpp17_contiguous_iterator<_Iter>::value>
16501660
struct __unwrap_iter_impl {
@@ -1674,6 +1684,20 @@ __unwrap_iter(_Iter __i) _NOEXCEPT
16741684
return _Impl::__apply(__i);
16751685
}
16761686

1687+
template<class _OrigIter>
1688+
_OrigIter __rewrap_iter(_OrigIter, _OrigIter __result)
1689+
{
1690+
return __result;
1691+
}
1692+
1693+
template<class _OrigIter, class _UnwrappedIter>
1694+
_OrigIter __rewrap_iter(_OrigIter __first, _UnwrappedIter __result)
1695+
{
1696+
// Precondition: __result is reachable from __first
1697+
// Precondition: _OrigIter is a contiguous iterator
1698+
return __first + (__result - _VSTD::__unwrap_iter(__first));
1699+
}
1700+
16771701
// copy
16781702

16791703
template <class _InputIterator, class _OutputIterator>
@@ -1716,11 +1740,12 @@ _OutputIterator
17161740
copy(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
17171741
{
17181742
if (__libcpp_is_constant_evaluated()) {
1719-
return _VSTD::__copy_constexpr(
1720-
_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result));
1743+
return _VSTD::__copy_constexpr(__first, __last, __result);
17211744
} else {
1722-
return _VSTD::__copy(
1723-
_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result));
1745+
return _VSTD::__rewrap_iter(__result,
1746+
_VSTD::__copy(_VSTD::__unwrap_iter(__first),
1747+
_VSTD::__unwrap_iter(__last),
1748+
_VSTD::__unwrap_iter(__result)));
17241749
}
17251750
}
17261751

@@ -1770,13 +1795,12 @@ copy_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
17701795
_BidirectionalIterator2 __result)
17711796
{
17721797
if (__libcpp_is_constant_evaluated()) {
1773-
return _VSTD::__copy_backward_constexpr(_VSTD::__unwrap_iter(__first),
1774-
_VSTD::__unwrap_iter(__last),
1775-
_VSTD::__unwrap_iter(__result));
1798+
return _VSTD::__copy_backward_constexpr(__first, __last, __result);
17761799
} else {
1777-
return _VSTD::__copy_backward(_VSTD::__unwrap_iter(__first),
1778-
_VSTD::__unwrap_iter(__last),
1779-
_VSTD::__unwrap_iter(__result));
1800+
return _VSTD::__rewrap_iter(__result,
1801+
_VSTD::__copy_backward(_VSTD::__unwrap_iter(__first),
1802+
_VSTD::__unwrap_iter(__last),
1803+
_VSTD::__unwrap_iter(__result)));
17801804
}
17811805
}
17821806

@@ -1843,8 +1867,6 @@ copy_n(_InputIterator __first, _Size __orig_n, _OutputIterator __result)
18431867

18441868
// move
18451869

1846-
// __move_constexpr exists so that __move doesn't call itself when delegating to the constexpr
1847-
// version of __move.
18481870
template <class _InputIterator, class _OutputIterator>
18491871
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
18501872
_OutputIterator
@@ -1873,8 +1895,6 @@ typename enable_if
18731895
>::type
18741896
__move(_Tp* __first, _Tp* __last, _Up* __result)
18751897
{
1876-
if (__libcpp_is_constant_evaluated())
1877-
return _VSTD::__move_constexpr(__first, __last, __result);
18781898
const size_t __n = static_cast<size_t>(__last - __first);
18791899
if (__n > 0)
18801900
_VSTD::memmove(__result, __first, __n * sizeof(_Up));
@@ -1886,13 +1906,18 @@ inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX17
18861906
_OutputIterator
18871907
move(_InputIterator __first, _InputIterator __last, _OutputIterator __result)
18881908
{
1889-
return _VSTD::__move(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result));
1909+
if (__libcpp_is_constant_evaluated()) {
1910+
return _VSTD::__move_constexpr(__first, __last, __result);
1911+
} else {
1912+
return _VSTD::__rewrap_iter(__result,
1913+
_VSTD::__move(_VSTD::__unwrap_iter(__first),
1914+
_VSTD::__unwrap_iter(__last),
1915+
_VSTD::__unwrap_iter(__result)));
1916+
}
18901917
}
18911918

18921919
// move_backward
18931920

1894-
// __move_backward_constexpr exists so that __move_backward doesn't call itself when delegating to
1895-
// the constexpr version of __move_backward.
18961921
template <class _InputIterator, class _OutputIterator>
18971922
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_AFTER_CXX14
18981923
_OutputIterator
@@ -1921,8 +1946,6 @@ typename enable_if
19211946
>::type
19221947
__move_backward(_Tp* __first, _Tp* __last, _Up* __result)
19231948
{
1924-
if (__libcpp_is_constant_evaluated())
1925-
return _VSTD::__move_backward_constexpr(__first, __last, __result);
19261949
const size_t __n = static_cast<size_t>(__last - __first);
19271950
if (__n > 0)
19281951
{
@@ -1938,7 +1961,14 @@ _BidirectionalIterator2
19381961
move_backward(_BidirectionalIterator1 __first, _BidirectionalIterator1 __last,
19391962
_BidirectionalIterator2 __result)
19401963
{
1941-
return _VSTD::__move_backward(_VSTD::__unwrap_iter(__first), _VSTD::__unwrap_iter(__last), _VSTD::__unwrap_iter(__result));
1964+
if (__libcpp_is_constant_evaluated()) {
1965+
return _VSTD::__move_backward_constexpr(__first, __last, __result);
1966+
} else {
1967+
return _VSTD::__rewrap_iter(__result,
1968+
_VSTD::__move_backward(_VSTD::__unwrap_iter(__first),
1969+
_VSTD::__unwrap_iter(__last),
1970+
_VSTD::__unwrap_iter(__result)));
1971+
}
19421972
}
19431973

19441974
// iter_swap

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy.pass.cpp

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -40,40 +40,49 @@ TEST_CONSTEXPR_CXX20 bool
4040
test()
4141
{
4242
test_copy<input_iterator<const int*>, output_iterator<int*> >();
43-
test_copy<input_iterator<const int*>, input_iterator<int*> >();
4443
test_copy<input_iterator<const int*>, forward_iterator<int*> >();
4544
test_copy<input_iterator<const int*>, bidirectional_iterator<int*> >();
4645
test_copy<input_iterator<const int*>, random_access_iterator<int*> >();
4746
test_copy<input_iterator<const int*>, int*>();
4847

4948
test_copy<forward_iterator<const int*>, output_iterator<int*> >();
50-
test_copy<forward_iterator<const int*>, input_iterator<int*> >();
5149
test_copy<forward_iterator<const int*>, forward_iterator<int*> >();
5250
test_copy<forward_iterator<const int*>, bidirectional_iterator<int*> >();
5351
test_copy<forward_iterator<const int*>, random_access_iterator<int*> >();
5452
test_copy<forward_iterator<const int*>, int*>();
5553

5654
test_copy<bidirectional_iterator<const int*>, output_iterator<int*> >();
57-
test_copy<bidirectional_iterator<const int*>, input_iterator<int*> >();
5855
test_copy<bidirectional_iterator<const int*>, forward_iterator<int*> >();
5956
test_copy<bidirectional_iterator<const int*>, bidirectional_iterator<int*> >();
6057
test_copy<bidirectional_iterator<const int*>, random_access_iterator<int*> >();
6158
test_copy<bidirectional_iterator<const int*>, int*>();
6259

6360
test_copy<random_access_iterator<const int*>, output_iterator<int*> >();
64-
test_copy<random_access_iterator<const int*>, input_iterator<int*> >();
6561
test_copy<random_access_iterator<const int*>, forward_iterator<int*> >();
6662
test_copy<random_access_iterator<const int*>, bidirectional_iterator<int*> >();
6763
test_copy<random_access_iterator<const int*>, random_access_iterator<int*> >();
6864
test_copy<random_access_iterator<const int*>, int*>();
6965

7066
test_copy<const int*, output_iterator<int*> >();
71-
test_copy<const int*, input_iterator<int*> >();
7267
test_copy<const int*, forward_iterator<int*> >();
7368
test_copy<const int*, bidirectional_iterator<int*> >();
7469
test_copy<const int*, random_access_iterator<int*> >();
7570
test_copy<const int*, int*>();
7671

72+
#if TEST_STD_VER > 17
73+
test_copy<input_iterator<const int*>, contiguous_iterator<int*>>();
74+
test_copy<forward_iterator<const int*>, contiguous_iterator<int*>>();
75+
test_copy<bidirectional_iterator<const int*>, contiguous_iterator<int*>>();
76+
test_copy<random_access_iterator<const int*>, contiguous_iterator<int*>>();
77+
test_copy<const int*, contiguous_iterator<int*>>();
78+
79+
test_copy<contiguous_iterator<const int*>, output_iterator<int*>>();
80+
test_copy<contiguous_iterator<const int*>, forward_iterator<int*>>();
81+
test_copy<contiguous_iterator<const int*>, bidirectional_iterator<int*>>();
82+
test_copy<contiguous_iterator<const int*>, random_access_iterator<int*>>();
83+
test_copy<contiguous_iterator<const int*>, int*>();
84+
#endif
85+
7786
return true;
7887
}
7988

libcxx/test/std/algorithms/alg.modifying.operations/alg.copy/copy_backward.pass.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,17 @@ test()
5353
test_copy_backward<const int*, random_access_iterator<int*> >();
5454
test_copy_backward<const int*, int*>();
5555

56+
#if TEST_STD_VER > 17
57+
test_copy_backward<contiguous_iterator<const int*>, bidirectional_iterator<int*>>();
58+
test_copy_backward<contiguous_iterator<const int*>, random_access_iterator<int*>>();
59+
test_copy_backward<contiguous_iterator<const int*>, int*>();
60+
61+
test_copy_backward<bidirectional_iterator<const int*>, contiguous_iterator<int*>>();
62+
test_copy_backward<random_access_iterator<const int*>, contiguous_iterator<int*>>();
63+
test_copy_backward<contiguous_iterator<const int*>, contiguous_iterator<int*>>();
64+
test_copy_backward<const int*, contiguous_iterator<int*>>();
65+
#endif
66+
5667
return true;
5768
}
5869

libcxx/test/std/algorithms/alg.modifying.operations/alg.move/move.pass.cpp

Lines changed: 36 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -64,106 +64,132 @@ test1()
6464
int main(int, char**)
6565
{
6666
test<input_iterator<const int*>, output_iterator<int*> >();
67-
test<input_iterator<const int*>, input_iterator<int*> >();
6867
test<input_iterator<const int*>, forward_iterator<int*> >();
6968
test<input_iterator<const int*>, bidirectional_iterator<int*> >();
7069
test<input_iterator<const int*>, random_access_iterator<int*> >();
7170
test<input_iterator<const int*>, int*>();
7271

7372
test<forward_iterator<const int*>, output_iterator<int*> >();
74-
test<forward_iterator<const int*>, input_iterator<int*> >();
7573
test<forward_iterator<const int*>, forward_iterator<int*> >();
7674
test<forward_iterator<const int*>, bidirectional_iterator<int*> >();
7775
test<forward_iterator<const int*>, random_access_iterator<int*> >();
7876
test<forward_iterator<const int*>, int*>();
7977

8078
test<bidirectional_iterator<const int*>, output_iterator<int*> >();
81-
test<bidirectional_iterator<const int*>, input_iterator<int*> >();
8279
test<bidirectional_iterator<const int*>, forward_iterator<int*> >();
8380
test<bidirectional_iterator<const int*>, bidirectional_iterator<int*> >();
8481
test<bidirectional_iterator<const int*>, random_access_iterator<int*> >();
8582
test<bidirectional_iterator<const int*>, int*>();
8683

8784
test<random_access_iterator<const int*>, output_iterator<int*> >();
88-
test<random_access_iterator<const int*>, input_iterator<int*> >();
8985
test<random_access_iterator<const int*>, forward_iterator<int*> >();
9086
test<random_access_iterator<const int*>, bidirectional_iterator<int*> >();
9187
test<random_access_iterator<const int*>, random_access_iterator<int*> >();
9288
test<random_access_iterator<const int*>, int*>();
9389

9490
test<const int*, output_iterator<int*> >();
95-
test<const int*, input_iterator<int*> >();
9691
test<const int*, forward_iterator<int*> >();
9792
test<const int*, bidirectional_iterator<int*> >();
9893
test<const int*, random_access_iterator<int*> >();
9994
test<const int*, int*>();
10095

10196
#if TEST_STD_VER >= 11
10297
test1<input_iterator<std::unique_ptr<int>*>, output_iterator<std::unique_ptr<int>*> >();
103-
test1<input_iterator<std::unique_ptr<int>*>, input_iterator<std::unique_ptr<int>*> >();
10498
test1<input_iterator<std::unique_ptr<int>*>, forward_iterator<std::unique_ptr<int>*> >();
10599
test1<input_iterator<std::unique_ptr<int>*>, bidirectional_iterator<std::unique_ptr<int>*> >();
106100
test1<input_iterator<std::unique_ptr<int>*>, random_access_iterator<std::unique_ptr<int>*> >();
107101
test1<input_iterator<std::unique_ptr<int>*>, std::unique_ptr<int>*>();
108102

109103
test1<forward_iterator<std::unique_ptr<int>*>, output_iterator<std::unique_ptr<int>*> >();
110-
test1<forward_iterator<std::unique_ptr<int>*>, input_iterator<std::unique_ptr<int>*> >();
111104
test1<forward_iterator<std::unique_ptr<int>*>, forward_iterator<std::unique_ptr<int>*> >();
112105
test1<forward_iterator<std::unique_ptr<int>*>, bidirectional_iterator<std::unique_ptr<int>*> >();
113106
test1<forward_iterator<std::unique_ptr<int>*>, random_access_iterator<std::unique_ptr<int>*> >();
114107
test1<forward_iterator<std::unique_ptr<int>*>, std::unique_ptr<int>*>();
115108

116109
test1<bidirectional_iterator<std::unique_ptr<int>*>, output_iterator<std::unique_ptr<int>*> >();
117-
test1<bidirectional_iterator<std::unique_ptr<int>*>, input_iterator<std::unique_ptr<int>*> >();
118110
test1<bidirectional_iterator<std::unique_ptr<int>*>, forward_iterator<std::unique_ptr<int>*> >();
119111
test1<bidirectional_iterator<std::unique_ptr<int>*>, bidirectional_iterator<std::unique_ptr<int>*> >();
120112
test1<bidirectional_iterator<std::unique_ptr<int>*>, random_access_iterator<std::unique_ptr<int>*> >();
121113
test1<bidirectional_iterator<std::unique_ptr<int>*>, std::unique_ptr<int>*>();
122114

123115
test1<random_access_iterator<std::unique_ptr<int>*>, output_iterator<std::unique_ptr<int>*> >();
124-
test1<random_access_iterator<std::unique_ptr<int>*>, input_iterator<std::unique_ptr<int>*> >();
125116
test1<random_access_iterator<std::unique_ptr<int>*>, forward_iterator<std::unique_ptr<int>*> >();
126117
test1<random_access_iterator<std::unique_ptr<int>*>, bidirectional_iterator<std::unique_ptr<int>*> >();
127118
test1<random_access_iterator<std::unique_ptr<int>*>, random_access_iterator<std::unique_ptr<int>*> >();
128119
test1<random_access_iterator<std::unique_ptr<int>*>, std::unique_ptr<int>*>();
129120

130121
test1<std::unique_ptr<int>*, output_iterator<std::unique_ptr<int>*> >();
131-
test1<std::unique_ptr<int>*, input_iterator<std::unique_ptr<int>*> >();
132122
test1<std::unique_ptr<int>*, forward_iterator<std::unique_ptr<int>*> >();
133123
test1<std::unique_ptr<int>*, bidirectional_iterator<std::unique_ptr<int>*> >();
134124
test1<std::unique_ptr<int>*, random_access_iterator<std::unique_ptr<int>*> >();
135125
test1<std::unique_ptr<int>*, std::unique_ptr<int>*>();
136126
#endif // TEST_STD_VER >= 11
137127

138128
#if TEST_STD_VER > 17
129+
test<input_iterator<const int*>, contiguous_iterator<int*>>();
130+
test<forward_iterator<const int*>, contiguous_iterator<int*>>();
131+
test<bidirectional_iterator<const int*>, contiguous_iterator<int*>>();
132+
test<random_access_iterator<const int*>, contiguous_iterator<int*>>();
133+
test<const int*, contiguous_iterator<int*>>();
134+
test<contiguous_iterator<const int*>, output_iterator<int*>>();
135+
test<contiguous_iterator<const int*>, forward_iterator<int*>>();
136+
test<contiguous_iterator<const int*>, bidirectional_iterator<int*>>();
137+
test<contiguous_iterator<const int*>, random_access_iterator<int*>>();
138+
test<contiguous_iterator<const int*>, int*>();
139+
test<contiguous_iterator<const int*>, contiguous_iterator<int*>>();
140+
141+
test1<input_iterator<std::unique_ptr<int>*>, contiguous_iterator<std::unique_ptr<int>*>>();
142+
test1<forward_iterator<std::unique_ptr<int>*>, contiguous_iterator<std::unique_ptr<int>*>>();
143+
test1<bidirectional_iterator<std::unique_ptr<int>*>, contiguous_iterator<std::unique_ptr<int>*>>();
144+
test1<random_access_iterator<std::unique_ptr<int>*>, contiguous_iterator<std::unique_ptr<int>*>>();
145+
test1<std::unique_ptr<int>*, contiguous_iterator<std::unique_ptr<int>*>>();
146+
test1<contiguous_iterator<std::unique_ptr<int>*>, output_iterator<std::unique_ptr<int>*>>();
147+
test1<contiguous_iterator<std::unique_ptr<int>*>, forward_iterator<std::unique_ptr<int>*>>();
148+
test1<contiguous_iterator<std::unique_ptr<int>*>, bidirectional_iterator<std::unique_ptr<int>*>>();
149+
test1<contiguous_iterator<std::unique_ptr<int>*>, random_access_iterator<std::unique_ptr<int>*>>();
150+
test1<contiguous_iterator<std::unique_ptr<int>*>, std::unique_ptr<int>*>();
151+
test1<contiguous_iterator<std::unique_ptr<int>*>, contiguous_iterator<std::unique_ptr<int>*>>();
152+
139153
static_assert(test<input_iterator<const int*>, input_iterator<int*> >());
140154
static_assert(test<input_iterator<const int*>, forward_iterator<int*> >());
141155
static_assert(test<input_iterator<const int*>, bidirectional_iterator<int*> >());
142156
static_assert(test<input_iterator<const int*>, random_access_iterator<int*> >());
157+
static_assert(test<input_iterator<const int*>, contiguous_iterator<int*> >());
143158
static_assert(test<input_iterator<const int*>, int*>());
144159

145160
static_assert(test<forward_iterator<const int*>, input_iterator<int*> >());
146161
static_assert(test<forward_iterator<const int*>, forward_iterator<int*> >());
147162
static_assert(test<forward_iterator<const int*>, bidirectional_iterator<int*> >());
148163
static_assert(test<forward_iterator<const int*>, random_access_iterator<int*> >());
164+
static_assert(test<forward_iterator<const int*>, contiguous_iterator<int*> >());
149165
static_assert(test<forward_iterator<const int*>, int*>());
150166

151167
static_assert(test<bidirectional_iterator<const int*>, input_iterator<int*> >());
152168
static_assert(test<bidirectional_iterator<const int*>, forward_iterator<int*> >());
153169
static_assert(test<bidirectional_iterator<const int*>, bidirectional_iterator<int*> >());
154170
static_assert(test<bidirectional_iterator<const int*>, random_access_iterator<int*> >());
171+
static_assert(test<bidirectional_iterator<const int*>, contiguous_iterator<int*> >());
155172
static_assert(test<bidirectional_iterator<const int*>, int*>());
156173

157174
static_assert(test<random_access_iterator<const int*>, input_iterator<int*> >());
158175
static_assert(test<random_access_iterator<const int*>, forward_iterator<int*> >());
159176
static_assert(test<random_access_iterator<const int*>, bidirectional_iterator<int*> >());
160177
static_assert(test<random_access_iterator<const int*>, random_access_iterator<int*> >());
178+
static_assert(test<random_access_iterator<const int*>, contiguous_iterator<int*> >());
161179
static_assert(test<random_access_iterator<const int*>, int*>());
162180

181+
static_assert(test<contiguous_iterator<const int*>, input_iterator<int*> >());
182+
static_assert(test<contiguous_iterator<const int*>, forward_iterator<int*> >());
183+
static_assert(test<contiguous_iterator<const int*>, bidirectional_iterator<int*> >());
184+
static_assert(test<contiguous_iterator<const int*>, random_access_iterator<int*> >());
185+
static_assert(test<contiguous_iterator<const int*>, contiguous_iterator<int*> >());
186+
static_assert(test<contiguous_iterator<const int*>, int*>());
187+
163188
static_assert(test<const int*, input_iterator<int*> >());
164189
static_assert(test<const int*, forward_iterator<int*> >());
165190
static_assert(test<const int*, bidirectional_iterator<int*> >());
166191
static_assert(test<const int*, random_access_iterator<int*> >());
192+
static_assert(test<const int*, contiguous_iterator<int*> >());
167193
static_assert(test<const int*, int*>());
168194
#endif // TEST_STD_VER > 17
169195

0 commit comments

Comments
 (0)