1 //===----------------------------------------------------------------------===// 2 // 3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. 4 // See https://llvm.org/LICENSE.txt for license information. 5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception 6 // 7 //===----------------------------------------------------------------------===// 8 9 // UNSUPPORTED: c++03, c++11, c++14, c++17 10 11 // std::ranges::begin 12 // std::ranges::cbegin 13 14 #include <ranges> 15 16 #include <cassert> 17 #include <utility> 18 #include "test_macros.h" 19 #include "test_iterators.h" 20 21 using RangeBeginT = decltype(std::ranges::begin); 22 using RangeCBeginT = decltype(std::ranges::cbegin); 23 24 static int globalBuff[8]; 25 26 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>); 27 static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>); 28 static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>); 29 static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>); 30 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[10]>); 31 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[10]>); 32 static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[]>); 33 static_assert( std::is_invocable_v<RangeCBeginT, int (&)[]>); 34 35 struct Incomplete; 36 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>); 37 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>); 38 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>); 39 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>); 40 41 static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>); 42 static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>); 43 static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>); 44 static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>); 45 46 // This case is IFNDR; we handle it SFINAE-friendly. 47 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>); 48 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>); 49 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>); 50 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>); 51 52 // This case is IFNDR; we handle it SFINAE-friendly. 53 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>); 54 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>); 55 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>); 56 LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>); 57 58 struct BeginMember { 59 int x; 60 constexpr const int *begin() const { return &x; } 61 }; 62 63 // Ensure that we can't call with rvalues with borrowing disabled. 64 static_assert( std::is_invocable_v<RangeBeginT, BeginMember &>); 65 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember &&>); 66 static_assert( std::is_invocable_v<RangeBeginT, BeginMember const&>); 67 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember const&&>); 68 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember &>); 69 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>); 70 static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>); 71 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember const&&>); 72 73 constexpr bool testReturnTypes() { 74 { 75 int *x[2]; 76 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int**); 77 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), int* const*); 78 } 79 { 80 int x[2][2]; 81 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), int(*)[2]); 82 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), const int(*)[2]); 83 } 84 { 85 struct Different { 86 char*& begin(); 87 short*& begin() const; 88 } x; 89 ASSERT_SAME_TYPE(decltype(std::ranges::begin(x)), char*); 90 ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(x)), short*); 91 } 92 return true; 93 } 94 95 constexpr bool testArray() { 96 int a[2]; 97 assert(std::ranges::begin(a) == a); 98 assert(std::ranges::cbegin(a) == a); 99 100 int b[2][2]; 101 assert(std::ranges::begin(b) == b); 102 assert(std::ranges::cbegin(b) == b); 103 104 BeginMember c[2]; 105 assert(std::ranges::begin(c) == c); 106 assert(std::ranges::cbegin(c) == c); 107 108 return true; 109 } 110 111 struct BeginMemberReturnsInt { 112 int begin() const; 113 }; 114 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsInt const&>); 115 116 struct BeginMemberReturnsVoidPtr { 117 const void *begin() const; 118 }; 119 static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsVoidPtr const&>); 120 121 struct EmptyBeginMember { 122 struct iterator {}; 123 iterator begin() const; 124 }; 125 static_assert(!std::is_invocable_v<RangeBeginT, EmptyBeginMember const&>); 126 127 struct PtrConvertibleBeginMember { 128 struct iterator { operator int*() const; }; 129 iterator begin() const; 130 }; 131 static_assert(!std::is_invocable_v<RangeBeginT, PtrConvertibleBeginMember const&>); 132 133 struct NonConstBeginMember { 134 int x; 135 constexpr int *begin() { return &x; } 136 }; 137 static_assert( std::is_invocable_v<RangeBeginT, NonConstBeginMember &>); 138 static_assert(!std::is_invocable_v<RangeBeginT, NonConstBeginMember const&>); 139 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>); 140 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>); 141 142 struct EnabledBorrowingBeginMember { 143 constexpr int *begin() const { return &globalBuff[0]; } 144 }; 145 template<> 146 inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingBeginMember> = true; 147 148 struct BeginMemberFunction { 149 int x; 150 constexpr const int *begin() const { return &x; } 151 friend int *begin(BeginMemberFunction const&); 152 }; 153 154 struct EmptyPtrBeginMember { 155 struct Empty {}; 156 Empty x; 157 constexpr const Empty *begin() const { return &x; } 158 }; 159 160 constexpr bool testBeginMember() { 161 BeginMember a; 162 assert(std::ranges::begin(a) == &a.x); 163 assert(std::ranges::cbegin(a) == &a.x); 164 static_assert(!std::is_invocable_v<RangeBeginT, BeginMember&&>); 165 static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember&&>); 166 167 NonConstBeginMember b; 168 assert(std::ranges::begin(b) == &b.x); 169 static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember&>); 170 171 EnabledBorrowingBeginMember c; 172 assert(std::ranges::begin(c) == &globalBuff[0]); 173 assert(std::ranges::cbegin(c) == &globalBuff[0]); 174 assert(std::ranges::begin(std::move(c)) == &globalBuff[0]); 175 assert(std::ranges::cbegin(std::move(c)) == &globalBuff[0]); 176 177 BeginMemberFunction d; 178 assert(std::ranges::begin(d) == &d.x); 179 assert(std::ranges::cbegin(d) == &d.x); 180 181 EmptyPtrBeginMember e; 182 assert(std::ranges::begin(e) == &e.x); 183 assert(std::ranges::cbegin(e) == &e.x); 184 185 return true; 186 } 187 188 189 struct BeginFunction { 190 int x; 191 friend constexpr const int *begin(BeginFunction const& bf) { return &bf.x; } 192 }; 193 static_assert( std::is_invocable_v<RangeBeginT, BeginFunction const&>); 194 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &&>); 195 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &>); 196 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>); 197 static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>); 198 199 struct BeginFunctionReturnsInt { 200 friend int begin(BeginFunctionReturnsInt const&); 201 }; 202 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsInt const&>); 203 204 struct BeginFunctionReturnsVoidPtr { 205 friend void *begin(BeginFunctionReturnsVoidPtr const&); 206 }; 207 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsVoidPtr const&>); 208 209 struct BeginFunctionReturnsPtrConvertible { 210 struct iterator { operator int*() const; }; 211 friend iterator begin(BeginFunctionReturnsPtrConvertible const&); 212 }; 213 static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertible const&>); 214 215 struct BeginFunctionByValue { 216 friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; } 217 }; 218 static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>); 219 220 struct BeginFunctionEnabledBorrowing { 221 friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; } 222 }; 223 template<> 224 inline constexpr bool std::ranges::enable_borrowed_range<BeginFunctionEnabledBorrowing> = true; 225 226 struct BeginFunctionReturnsEmptyPtr { 227 struct Empty {}; 228 Empty x; 229 friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; } 230 }; 231 232 struct BeginFunctionWithDataMember { 233 int x; 234 int begin; 235 friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; } 236 }; 237 238 struct BeginFunctionWithPrivateBeginMember { 239 int y; 240 friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; } 241 private: 242 const int *begin() const; 243 }; 244 245 constexpr bool testBeginFunction() { 246 BeginFunction a{}; 247 const BeginFunction aa{}; 248 static_assert(!std::invocable<RangeBeginT, decltype((a))>); 249 assert(std::ranges::cbegin(a) == &a.x); 250 assert(std::ranges::begin(aa) == &aa.x); 251 assert(std::ranges::cbegin(aa) == &aa.x); 252 253 BeginFunctionByValue b{}; 254 const BeginFunctionByValue bb{}; 255 assert(std::ranges::begin(b) == &globalBuff[1]); 256 assert(std::ranges::cbegin(b) == &globalBuff[1]); 257 assert(std::ranges::begin(bb) == &globalBuff[1]); 258 assert(std::ranges::cbegin(bb) == &globalBuff[1]); 259 260 BeginFunctionEnabledBorrowing c{}; 261 const BeginFunctionEnabledBorrowing cc{}; 262 assert(std::ranges::begin(std::move(c)) == &globalBuff[2]); 263 assert(std::ranges::cbegin(std::move(c)) == &globalBuff[2]); 264 assert(std::ranges::begin(std::move(cc)) == &globalBuff[2]); 265 assert(std::ranges::cbegin(std::move(cc)) == &globalBuff[2]); 266 267 BeginFunctionReturnsEmptyPtr d{}; 268 const BeginFunctionReturnsEmptyPtr dd{}; 269 static_assert(!std::invocable<RangeBeginT, decltype((d))>); 270 assert(std::ranges::cbegin(d) == &d.x); 271 assert(std::ranges::begin(dd) == &dd.x); 272 assert(std::ranges::cbegin(dd) == &dd.x); 273 274 BeginFunctionWithDataMember e{}; 275 const BeginFunctionWithDataMember ee{}; 276 static_assert(!std::invocable<RangeBeginT, decltype((e))>); 277 assert(std::ranges::begin(ee) == &ee.x); 278 assert(std::ranges::cbegin(e) == &e.x); 279 assert(std::ranges::cbegin(ee) == &ee.x); 280 281 BeginFunctionWithPrivateBeginMember f{}; 282 const BeginFunctionWithPrivateBeginMember ff{}; 283 static_assert(!std::invocable<RangeBeginT, decltype((f))>); 284 assert(std::ranges::cbegin(f) == &f.y); 285 assert(std::ranges::begin(ff) == &ff.y); 286 assert(std::ranges::cbegin(ff) == &ff.y); 287 288 return true; 289 } 290 291 292 ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>())); 293 ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>())); 294 295 struct NoThrowMemberBegin { 296 ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw 297 } ntmb; 298 static_assert(noexcept(std::ranges::begin(ntmb))); 299 static_assert(noexcept(std::ranges::cbegin(ntmb))); 300 301 struct NoThrowADLBegin { 302 friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw 303 friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept; 304 } ntab; 305 static_assert(noexcept(std::ranges::begin(ntab))); 306 static_assert(noexcept(std::ranges::cbegin(ntab))); 307 308 struct NoThrowMemberBeginReturnsRef { 309 ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw 310 } ntmbrr; 311 static_assert(!noexcept(std::ranges::begin(ntmbrr))); 312 static_assert(!noexcept(std::ranges::cbegin(ntmbrr))); 313 314 struct BeginReturnsArrayRef { 315 auto begin() const noexcept -> int(&)[10]; 316 } brar; 317 static_assert(noexcept(std::ranges::begin(brar))); 318 static_assert(noexcept(std::ranges::cbegin(brar))); 319 320 // Test ADL-proofing. 321 struct Incomplete; 322 template<class T> struct Holder { T t; }; 323 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>); 324 static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*&>); 325 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>); 326 static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>); 327 328 int main(int, char**) { 329 static_assert(testReturnTypes()); 330 331 testArray(); 332 static_assert(testArray()); 333 334 testBeginMember(); 335 static_assert(testBeginMember()); 336 337 testBeginFunction(); 338 static_assert(testBeginFunction()); 339 340 return 0; 341 } 342