342 lines · cpp
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-exception6//7//===----------------------------------------------------------------------===//8 9// UNSUPPORTED: c++03, c++11, c++14, c++1710 11// std::ranges::begin12// std::ranges::cbegin13 14#include <ranges>15 16#include <cassert>17#include <utility>18#include "test_macros.h"19#include "test_iterators.h"20 21using RangeBeginT = decltype(std::ranges::begin);22using RangeCBeginT = decltype(std::ranges::cbegin);23 24static int globalBuff[8];25 26static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>);27static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>);28static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>);29static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>);30static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[10]>);31static_assert( std::is_invocable_v<RangeCBeginT, int (&)[10]>);32static_assert(!std::is_invocable_v<RangeCBeginT, int (&&)[]>);33static_assert( std::is_invocable_v<RangeCBeginT, int (&)[]>);34 35struct Incomplete;36static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);37static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[]>);38static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);39static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[]>);40 41static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[10]>);42static_assert(!std::is_invocable_v<RangeBeginT, const Incomplete(&&)[10]>);43static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[10]>);44static_assert(!std::is_invocable_v<RangeCBeginT, const Incomplete(&&)[10]>);45 46// This case is IFNDR; we handle it SFINAE-friendly.47LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[]>);48LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[]>);49LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[]>);50LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[]>);51 52// This case is IFNDR; we handle it SFINAE-friendly.53LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, Incomplete(&)[10]>);54LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeBeginT, const Incomplete(&)[10]>);55LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, Incomplete(&)[10]>);56LIBCPP_STATIC_ASSERT(!std::is_invocable_v<RangeCBeginT, const Incomplete(&)[10]>);57 58struct 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.64static_assert( std::is_invocable_v<RangeBeginT, BeginMember &>);65static_assert(!std::is_invocable_v<RangeBeginT, BeginMember &&>);66static_assert( std::is_invocable_v<RangeBeginT, BeginMember const&>);67static_assert(!std::is_invocable_v<RangeBeginT, BeginMember const&&>);68static_assert( std::is_invocable_v<RangeCBeginT, BeginMember &>);69static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember &&>);70static_assert( std::is_invocable_v<RangeCBeginT, BeginMember const&>);71static_assert(!std::is_invocable_v<RangeCBeginT, BeginMember const&&>);72 73constexpr 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 95constexpr 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 111struct BeginMemberReturnsInt {112 int begin() const;113};114static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsInt const&>);115 116struct BeginMemberReturnsVoidPtr {117 const void *begin() const;118};119static_assert(!std::is_invocable_v<RangeBeginT, BeginMemberReturnsVoidPtr const&>);120 121struct EmptyBeginMember {122 struct iterator {};123 iterator begin() const;124};125static_assert(!std::is_invocable_v<RangeBeginT, EmptyBeginMember const&>);126 127struct PtrConvertibleBeginMember {128 struct iterator { operator int*() const; };129 iterator begin() const;130};131static_assert(!std::is_invocable_v<RangeBeginT, PtrConvertibleBeginMember const&>);132 133struct NonConstBeginMember {134 int x;135 constexpr int *begin() { return &x; }136};137static_assert( std::is_invocable_v<RangeBeginT, NonConstBeginMember &>);138static_assert(!std::is_invocable_v<RangeBeginT, NonConstBeginMember const&>);139static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember &>);140static_assert(!std::is_invocable_v<RangeCBeginT, NonConstBeginMember const&>);141 142struct EnabledBorrowingBeginMember {143 constexpr int *begin() const { return &globalBuff[0]; }144};145template<>146inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingBeginMember> = true;147 148struct BeginMemberFunction {149 int x;150 constexpr const int *begin() const { return &x; }151 friend int *begin(BeginMemberFunction const&);152};153 154struct EmptyPtrBeginMember {155 struct Empty {};156 Empty x;157 constexpr const Empty *begin() const { return &x; }158};159 160constexpr 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 189struct BeginFunction {190 int x;191 friend constexpr const int *begin(BeginFunction const& bf) { return &bf.x; }192};193static_assert( std::is_invocable_v<RangeBeginT, BeginFunction const&>);194static_assert(!std::is_invocable_v<RangeBeginT, BeginFunction &&>);195static_assert(std::is_invocable_v<RangeBeginT, BeginFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic196static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction const&>);197static_assert( std::is_invocable_v<RangeCBeginT, BeginFunction &>);198 199struct BeginFunctionReturnsInt {200 friend int begin(BeginFunctionReturnsInt const&);201};202static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsInt const&>);203 204struct BeginFunctionReturnsVoidPtr {205 friend void *begin(BeginFunctionReturnsVoidPtr const&);206};207static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsVoidPtr const&>);208 209struct BeginFunctionReturnsPtrConvertible {210 struct iterator { operator int*() const; };211 friend iterator begin(BeginFunctionReturnsPtrConvertible const&);212};213static_assert(!std::is_invocable_v<RangeBeginT, BeginFunctionReturnsPtrConvertible const&>);214 215struct BeginFunctionByValue {216 friend constexpr int *begin(BeginFunctionByValue) { return &globalBuff[1]; }217};218static_assert(!std::is_invocable_v<RangeCBeginT, BeginFunctionByValue>);219 220struct BeginFunctionEnabledBorrowing {221 friend constexpr int *begin(BeginFunctionEnabledBorrowing) { return &globalBuff[2]; }222};223template<>224inline constexpr bool std::ranges::enable_borrowed_range<BeginFunctionEnabledBorrowing> = true;225 226struct BeginFunctionReturnsEmptyPtr {227 struct Empty {};228 Empty x;229 friend constexpr const Empty *begin(BeginFunctionReturnsEmptyPtr const& bf) { return &bf.x; }230};231 232struct BeginFunctionWithDataMember {233 int x;234 int begin;235 friend constexpr const int *begin(BeginFunctionWithDataMember const& bf) { return &bf.x; }236};237 238struct BeginFunctionWithPrivateBeginMember {239 int y;240 friend constexpr const int *begin(BeginFunctionWithPrivateBeginMember const& bf) { return &bf.y; }241private:242 const int *begin() const;243};244 245constexpr bool testBeginFunction() {246 BeginFunction a{};247 const BeginFunction aa{};248 assert(std::ranges::begin(a) == &a.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic249 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 assert(std::ranges::begin(d) == &d.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic270 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 assert(std::ranges::begin(e) == &e.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic277 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 assert(std::ranges::begin(f) == &f.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic284 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 292ASSERT_NOEXCEPT(std::ranges::begin(std::declval<int (&)[10]>()));293ASSERT_NOEXCEPT(std::ranges::cbegin(std::declval<int (&)[10]>()));294 295struct NoThrowMemberBegin {296 ThrowingIterator<int> begin() const noexcept; // auto(t.begin()) doesn't throw297} ntmb;298static_assert(noexcept(std::ranges::begin(ntmb)));299static_assert(noexcept(std::ranges::cbegin(ntmb)));300 301struct NoThrowADLBegin {302 friend ThrowingIterator<int> begin(NoThrowADLBegin&) noexcept; // auto(begin(t)) doesn't throw303 friend ThrowingIterator<int> begin(const NoThrowADLBegin&) noexcept;304} ntab;305static_assert(noexcept(std::ranges::begin(ntab)));306static_assert(noexcept(std::ranges::cbegin(ntab)));307 308struct NoThrowMemberBeginReturnsRef {309 ThrowingIterator<int>& begin() const noexcept; // auto(t.begin()) may throw310} ntmbrr;311static_assert(!noexcept(std::ranges::begin(ntmbrr)));312static_assert(!noexcept(std::ranges::cbegin(ntmbrr)));313 314struct BeginReturnsArrayRef {315 auto begin() const noexcept -> int(&)[10];316} brar;317static_assert(noexcept(std::ranges::begin(brar)));318static_assert(noexcept(std::ranges::cbegin(brar)));319 320// Test ADL-proofing.321struct Incomplete;322template<class T> struct Holder { T t; };323static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*>);324static_assert(!std::is_invocable_v<RangeBeginT, Holder<Incomplete>*&>);325static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*>);326static_assert(!std::is_invocable_v<RangeCBeginT, Holder<Incomplete>*&>);327 328int 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