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