Skip to content

Commit cb950c9

Browse files
authored
[libc++] Make sure we implement and test LWG2280 properly (#67670)
We did not mark std::begin/std::end as noexcept for C-style arrays, we did not have conditional noexcept on cbegin/cend, and we did not mark array cbegin/cend as constexpr in all Standard modes. Since this is a LWG issue, we should implement it as a DR in all Standard modes as usual. This patch fixes these issues and adds test coverage. Fixes #67471.
1 parent deb429e commit cb950c9

File tree

4 files changed

+55
-26
lines changed

4 files changed

+55
-26
lines changed

libcxx/include/__iterator/access.h

Lines changed: 9 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,19 +20,13 @@
2020
_LIBCPP_BEGIN_NAMESPACE_STD
2121

2222
template <class _Tp, size_t _Np>
23-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
24-
_Tp*
25-
begin(_Tp (&__array)[_Np])
26-
{
27-
return __array;
23+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* begin(_Tp (&__array)[_Np]) _NOEXCEPT {
24+
return __array;
2825
}
2926

3027
template <class _Tp, size_t _Np>
31-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
32-
_Tp*
33-
end(_Tp (&__array)[_Np])
34-
{
35-
return __array + _Np;
28+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR _Tp* end(_Tp (&__array)[_Np]) _NOEXCEPT {
29+
return __array + _Np;
3630
}
3731

3832
#if !defined(_LIBCPP_CXX03_LANG)
@@ -72,17 +66,14 @@ end(const _Cp& __c) -> decltype(__c.end())
7266
#if _LIBCPP_STD_VER >= 14
7367

7468
template <class _Cp>
75-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
76-
auto cbegin(const _Cp& __c) -> decltype(_VSTD::begin(__c))
77-
{
78-
return _VSTD::begin(__c);
69+
_LIBCPP_HIDE_FROM_ABI constexpr auto cbegin(const _Cp& __c) noexcept(noexcept(std::begin(__c)))
70+
-> decltype(std::begin(__c)) {
71+
return std::begin(__c);
7972
}
8073

8174
template <class _Cp>
82-
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR_SINCE_CXX14
83-
auto cend(const _Cp& __c) -> decltype(_VSTD::end(__c))
84-
{
85-
return _VSTD::end(__c);
75+
_LIBCPP_HIDE_FROM_ABI constexpr auto cend(const _Cp& __c) noexcept(noexcept(std::end(__c))) -> decltype(std::end(__c)) {
76+
return std::end(__c);
8677
}
8778

8879
#endif

libcxx/include/iterator

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -638,11 +638,11 @@ template <class C> constexpr auto begin(C& c) -> decltype(c.begin());
638638
template <class C> constexpr auto begin(const C& c) -> decltype(c.begin()); // constexpr since C++17
639639
template <class C> constexpr auto end(C& c) -> decltype(c.end()); // constexpr since C++17
640640
template <class C> constexpr auto end(const C& c) -> decltype(c.end()); // constexpr since C++17
641-
template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept; // constexpr since C++14
642-
template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept; // constexpr since C++14
641+
template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
642+
template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
643643
644-
template <class C> constexpr auto cbegin(const C& c) -> decltype(std::begin(c)); // C++14
645-
template <class C> constexpr auto cend(const C& c) -> decltype(std::end(c)); // C++14
644+
template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
645+
template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c)); // C++14
646646
template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin()); // C++14, constexpr since C++17
647647
template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin()); // C++14, constexpr since C++17
648648
template <class C> constexpr auto rend(C& c) -> decltype(c.rend()); // C++14, constexpr since C++17

libcxx/test/std/iterators/iterator.range/begin-end.array.pass.cpp

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@
1010

1111
// <iterator>
1212
//
13-
// template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept; // constexpr since C++14
14-
// template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept; // constexpr since C++14
13+
// template <class T, size_t N> constexpr T* begin(T (&array)[N]) noexcept;
14+
// template <class T, size_t N> constexpr T* end(T (&array)[N]) noexcept;
1515
//
1616
// template <class T, size_t N> constexpr reverse_iterator<T*> rbegin(T (&array)[N]); // C++14, constexpr since C++17
1717
// template <class T, size_t N> constexpr reverse_iterator<T*> rend(T (&array)[N]); // C++14, constexpr since C++17
@@ -27,16 +27,20 @@ TEST_CONSTEXPR_CXX14 bool test() {
2727

2828
// std::begin(T (&)[N]) / std::end(T (&)[N])
2929
{
30+
ASSERT_NOEXCEPT(std::begin(a));
3031
ASSERT_SAME_TYPE(decltype(std::begin(a)), int*);
3132
assert(std::begin(a) == a);
3233

34+
ASSERT_NOEXCEPT(std::end(a));
3335
ASSERT_SAME_TYPE(decltype(std::end(a)), int*);
3436
assert(std::end(a) == a + 3);
3537

3638
// kind of overkill since it follows from the definition, but worth testing
39+
ASSERT_NOEXCEPT(std::begin(ca));
3740
ASSERT_SAME_TYPE(decltype(std::begin(ca)), const int*);
3841
assert(std::begin(ca) == ca);
3942

43+
ASSERT_NOEXCEPT(std::end(ca));
4044
ASSERT_SAME_TYPE(decltype(std::end(ca)), const int*);
4145
assert(std::end(ca) == ca + 3);
4246
}
@@ -79,5 +83,14 @@ int main(int, char**) {
7983
static_assert(test_r(), "");
8084
#endif
8185

86+
// Make sure std::begin(T (&)[N]) and std::end(T (&)[N]) are constexpr in C++11 too (see LWG2280).
87+
{
88+
static constexpr int a[] = {1, 2, 3};
89+
constexpr auto b = std::begin(a);
90+
assert(b == a);
91+
constexpr auto e = std::end(a);
92+
assert(e == a + 3);
93+
}
94+
8295
return 0;
8396
}

libcxx/test/std/iterators/iterator.range/begin-end.container.pass.cpp

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,8 @@
1515
// template <class C> constexpr auto end(C& c) -> decltype(c.end()); // constexpr since C++17
1616
// template <class C> constexpr auto end(const C& c) -> decltype(c.end()); // constexpr since C++17
1717
//
18-
// template <class C> constexpr auto cbegin(const C& c) -> decltype(std::begin(c)); // C++14
19-
// template <class C> constexpr auto cend(const C& c) -> decltype(std::end(c)); // C++14
18+
// template <class C> constexpr auto cbegin(const C& c) noexcept(see-below) -> decltype(std::begin(c)); // C++14
19+
// template <class C> constexpr auto cend(const C& c) noexcept(see-below) -> decltype(std::end(c)); // C++14
2020
// template <class C> constexpr auto rbegin(C& c) -> decltype(c.rbegin()); // C++14, constexpr since C++17
2121
// template <class C> constexpr auto rbegin(const C& c) -> decltype(c.rbegin()); // C++14, constexpr since C++17
2222
// template <class C> constexpr auto rend(C& c) -> decltype(c.rend()); // C++14, constexpr since C++17
@@ -127,5 +127,30 @@ int main(int, char**) {
127127
static_assert(test<std::array<int, 3>>());
128128
#endif
129129

130+
// Note: Properly testing the conditional noexcept-ness propagation in std::cbegin and std::cend
131+
// requires using C-style arrays, because those are the only ones with a noexcept std::begin
132+
// and std::end inside namespace std
133+
#if TEST_STD_VER >= 14
134+
{
135+
int a[] = {1, 2, 3};
136+
auto const& ca = a;
137+
ASSERT_NOEXCEPT(std::cbegin(ca));
138+
ASSERT_NOEXCEPT(std::cend(ca));
139+
140+
// kind of overkill, but whatever
141+
ASSERT_NOEXCEPT(std::cbegin(a));
142+
ASSERT_NOEXCEPT(std::cend(a));
143+
}
144+
145+
// Make sure std::cbegin and std::cend are constexpr in C++14 too (see LWG2280).
146+
{
147+
static constexpr int a[] = {1, 2, 3};
148+
constexpr auto b = std::cbegin(a);
149+
assert(b == a);
150+
constexpr auto e = std::cend(a);
151+
assert(e == a + 3);
152+
}
153+
#endif
154+
130155
return 0;
131156
}

0 commit comments

Comments
 (0)