forked from OSchip/llvm-project
[libc++] [ranges] Remove the static_assert from ranges::begin and ranges::end.
As discussed with ldionne. The problem with this static_assert is that it makes ranges::begin a pitfall for anyone ever to use inside a constraint or decltype. Many Ranges things, such as ranges::size, are specified as "Does X if X is well-formed, or else Y if Y is well-formed, or else `ranges::end(t) - ranges::begin(t)` if that is well-formed, or else..." And if there's a static_assert hidden inside `ranges::begin(t)`, then you get a hard error as soon as you ask the question -- even if the answer would have been "no, that's not well-formed"! Constraining on `requires { t + 0; }` or `requires { t + N; }` is verboten because of https://gcc.gnu.org/bugzilla/show_bug.cgi?id=103700 . For ranges::begin, we can just decay to a pointer even in the incomplete-type case. For ranges::end, we can safely constrain on `sizeof(*t)`. Yes, this means that an array of incomplete type has a `ranges::begin` but no `ranges::end`... just like an unbounded array of complete type. This is a valid manifestation of IFNDR. All of the new libcxx/test/std/ cases are mandatory behavior, as far as I'm aware. Tests for the IFNDR cases in ranges::begin and ranges::end remain in `libcxx/test/libcxx/`. The similar tests for ranges::empty and ranges::data were simply wrong, AFAIK. Differential Revision: https://reviews.llvm.org/D115838
This commit is contained in:
parent
9075009d1f
commit
8ad364ad21
|
@ -27,15 +27,10 @@ _LIBCPP_BEGIN_NAMESPACE_STD
|
|||
|
||||
#if !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
// clang-format off
|
||||
|
||||
namespace ranges {
|
||||
template <class _Tp>
|
||||
concept __can_borrow =
|
||||
is_lvalue_reference_v<_Tp> || enable_borrowed_range<remove_cvref_t<_Tp> >;
|
||||
|
||||
template<class _Tp>
|
||||
concept __is_complete = requires { sizeof(_Tp); };
|
||||
} // namespace ranges
|
||||
|
||||
// [range.access.begin]
|
||||
|
@ -61,15 +56,10 @@ namespace ranges::__begin {
|
|||
|
||||
struct __fn {
|
||||
template <class _Tp>
|
||||
requires is_array_v<remove_cv_t<_Tp>>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept {
|
||||
constexpr bool __complete = __is_complete<iter_value_t<_Tp> >;
|
||||
if constexpr (__complete) { // used to disable cryptic diagnostic
|
||||
return __t + 0;
|
||||
}
|
||||
else {
|
||||
static_assert(__complete, "`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type.");
|
||||
}
|
||||
requires is_array_v<remove_cv_t<_Tp>>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp& __t) const noexcept
|
||||
{
|
||||
return __t;
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
|
@ -127,14 +117,10 @@ namespace ranges::__end {
|
|||
class __fn {
|
||||
public:
|
||||
template <class _Tp, size_t _Np>
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept {
|
||||
constexpr bool __complete = __is_complete<remove_cv_t<_Tp> >;
|
||||
if constexpr (__complete) { // used to disable cryptic diagnostic
|
||||
return __t + _Np;
|
||||
}
|
||||
else {
|
||||
static_assert(__complete, "`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type.");
|
||||
}
|
||||
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Tp (&__t)[_Np]) const noexcept
|
||||
requires (sizeof(*__t) != 0) // Disallow incomplete element types.
|
||||
{
|
||||
return __t + _Np;
|
||||
}
|
||||
|
||||
template <class _Tp>
|
||||
|
@ -209,8 +195,6 @@ namespace ranges::inline __cpo {
|
|||
inline constexpr auto cend = __cend::__fn{};
|
||||
} // namespace ranges::__cpo
|
||||
|
||||
// clang-format off
|
||||
|
||||
#endif // !defined(_LIBCPP_HAS_NO_RANGES)
|
||||
|
||||
_LIBCPP_END_NAMESPACE_STD
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// RUN: %{cxx} %{flags} %{compile_flags} -c %s -o %t.tu1.o -DTU1
|
||||
// RUN: %{cxx} %{flags} %{compile_flags} -c %s -o %t.tu2.o -DTU2
|
||||
// RUN: %{cxx} %t.tu1.o %t.tu2.o %{flags} %{link_flags} -o %t.exe
|
||||
// RUN: %{exec} %t.exe
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++-specific behavior that we handle the IFNDR case for ranges::begin
|
||||
// by returning the beginning of the array-of-incomplete-type.
|
||||
// Use two translation units so that `Incomplete` really is never completed
|
||||
// at any point within TU2, but the array `bounded` is still given a definition
|
||||
// (in TU1) to avoid an "undefined reference" error from the linker.
|
||||
// All of the actually interesting stuff takes place within TU2.
|
||||
|
||||
#include <ranges>
|
||||
#include <cassert>
|
||||
|
||||
#include "test_macros.h"
|
||||
|
||||
#if defined(TU1)
|
||||
|
||||
struct Incomplete {};
|
||||
Incomplete bounded[10];
|
||||
Incomplete unbounded[10];
|
||||
|
||||
#else // defined(TU1)
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
constexpr bool test()
|
||||
{
|
||||
{
|
||||
extern Incomplete bounded[10];
|
||||
assert(std::ranges::begin(bounded) == bounded);
|
||||
assert(std::ranges::cbegin(bounded) == bounded);
|
||||
assert(std::ranges::begin(std::as_const(bounded)) == bounded);
|
||||
assert(std::ranges::cbegin(std::as_const(bounded)) == bounded);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(bounded)), Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(bounded)), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(std::as_const(bounded))), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(std::as_const(bounded))), const Incomplete*);
|
||||
}
|
||||
{
|
||||
extern Incomplete unbounded[];
|
||||
assert(std::ranges::begin(unbounded) == unbounded);
|
||||
assert(std::ranges::cbegin(unbounded) == unbounded);
|
||||
assert(std::ranges::begin(std::as_const(unbounded)) == unbounded);
|
||||
assert(std::ranges::cbegin(std::as_const(unbounded)) == unbounded);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(unbounded)), Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(unbounded)), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::begin(std::as_const(unbounded))), const Incomplete*);
|
||||
ASSERT_SAME_TYPE(decltype(std::ranges::cbegin(std::as_const(unbounded))), const Incomplete*);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
static_assert(test());
|
||||
}
|
||||
|
||||
#endif // defined(TU1)
|
|
@ -0,0 +1,46 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++-specific behavior that we handle the IFNDR case for ranges::end
|
||||
// by being SFINAE-friendly.
|
||||
|
||||
#include <ranges>
|
||||
#include <cassert>
|
||||
#include <type_traits>
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
constexpr bool test()
|
||||
{
|
||||
{
|
||||
extern Incomplete bounded[10];
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::end), decltype((bounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype((bounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::end), decltype(std::as_const(bounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype(std::as_const(bounded))>));
|
||||
}
|
||||
{
|
||||
extern Incomplete unbounded[];
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::end), decltype((unbounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype((unbounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::end), decltype(std::as_const(unbounded))>));
|
||||
assert((!std::is_invocable_v<decltype(std::ranges::cend), decltype(std::as_const(unbounded))>));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
int main(int, char**)
|
||||
{
|
||||
test();
|
||||
static_assert(test());
|
||||
}
|
|
@ -1,36 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::begin on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using begin_t = decltype(std::ranges::begin);
|
||||
|
||||
template <class T> void f() requires std::invocable<begin_t&, T> { }
|
||||
template <class T> void f() { }
|
||||
|
||||
void test() {
|
||||
struct incomplete;
|
||||
f<incomplete(&)[]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[10]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[2][2]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
|
||||
// This is okay because calling `std::ranges::begin` on any rvalue is ill-formed.
|
||||
f<incomplete(&&)[10]>();
|
||||
}
|
|
@ -1,32 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::cbegin on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using cbegin_t = decltype(std::ranges::cbegin);
|
||||
|
||||
template <class T> void f() requires std::invocable<cbegin_t&, T> { }
|
||||
template <class T> void f() { }
|
||||
|
||||
void test() {
|
||||
struct incomplete;
|
||||
f<incomplete(&)[10]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
|
||||
// This is okay because calling `std::ranges::end` on any rvalue is ill-formed.
|
||||
f<incomplete(&&)[10]>();
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::cend on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using cend_t = decltype(std::ranges::cend);
|
||||
|
||||
template <class T> void f() requires std::invocable<cend_t&, T> { }
|
||||
template <class T> void f() { }
|
||||
|
||||
void test() {
|
||||
struct incomplete;
|
||||
f<incomplete(&)[]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
// expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[10]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
// expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[2][2]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
|
||||
// This is okay because calling `std::ranges::end` on any rvalue is ill-formed.
|
||||
f<incomplete(&&)[10]>();
|
||||
}
|
|
@ -1,38 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::end on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
using end_t = decltype(std::ranges::end);
|
||||
|
||||
template <class T> void f() requires std::invocable<end_t&, T> { }
|
||||
template <class T> void f() { }
|
||||
|
||||
void test() {
|
||||
struct incomplete;
|
||||
f<incomplete(&)[]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
// expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[10]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
// expected-error@*:* {{"`std::ranges::end` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
f<incomplete(&)[2][2]>();
|
||||
// expected-error@*:* {{"`std::ranges::begin` is SFINAE-unfriendly on arrays of an incomplete type."}}
|
||||
|
||||
// This is okay because calling `std::ranges::end` on any rvalue is ill-formed.
|
||||
f<incomplete(&&)[10]>();
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::data on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
void f(Incomplete arr[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&&arr)[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f2(Incomplete arr[2]) {
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[2]) {
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&&arr)[2]) {
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[2][2]) {
|
||||
// expected-error@*:* {{no matching function for call}}
|
||||
std::ranges::data(arr);
|
||||
}
|
|
@ -1,53 +0,0 @@
|
|||
//===----------------------------------------------------------------------===//
|
||||
//
|
||||
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
|
||||
// See https://llvm.org/LICENSE.txt for license information.
|
||||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
|
||||
//
|
||||
//===----------------------------------------------------------------------===//
|
||||
|
||||
// UNSUPPORTED: c++03, c++11, c++14, c++17
|
||||
// UNSUPPORTED: libcpp-no-concepts
|
||||
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
|
||||
|
||||
// Test the libc++ specific behavior that we provide a better diagnostic when calling
|
||||
// std::ranges::empty on an array of incomplete type.
|
||||
|
||||
#include <ranges>
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
void f(Incomplete arr[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
// expected-error@*:* {{call to deleted function call operator in type}}
|
||||
// expected-error@*:* {{attempt to use a deleted function}}
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&&arr)[]) {
|
||||
// expected-error@*:* {{is SFINAE-unfriendly on arrays of an incomplete type.}}
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f2(Incomplete arr[2]) {
|
||||
// expected-error@*:* {{call to deleted function call operator in type}}
|
||||
// expected-error@*:* {{attempt to use a deleted function}}
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[2]) {
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&&arr)[2]) {
|
||||
std::ranges::begin(arr);
|
||||
}
|
||||
|
||||
void f(Incomplete(&arr)[2][2]) {
|
||||
std::ranges::begin(arr);
|
||||
}
|
|
@ -23,13 +23,17 @@ using RangeCBeginT = decltype(std::ranges::cbegin)&;
|
|||
|
||||
static int globalBuff[8];
|
||||
|
||||
struct Incomplete;
|
||||
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[10]>);
|
||||
static_assert( std::is_invocable_v<RangeBeginT, int (&)[10]>);
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, int (&&)[]>);
|
||||
static_assert( std::is_invocable_v<RangeBeginT, int (&)[]>);
|
||||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeBeginT, Incomplete(&&)[42]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCBeginT, Incomplete(&&)[42]>);
|
||||
|
||||
struct BeginMember {
|
||||
int x;
|
||||
constexpr const int *begin() const { return &x; }
|
||||
|
|
|
@ -28,6 +28,12 @@ static_assert(!std::is_invocable_v<RangeEndT, int (&)[]>);
|
|||
static_assert(!std::is_invocable_v<RangeEndT, int (&&)[10]>);
|
||||
static_assert( std::is_invocable_v<RangeEndT, int (&)[10]>);
|
||||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeEndT, Incomplete(&&)[42]>);
|
||||
static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeCEndT, Incomplete(&&)[42]>);
|
||||
|
||||
struct EndMember {
|
||||
int x;
|
||||
constexpr const int *begin() const { return nullptr; }
|
||||
|
|
|
@ -30,6 +30,17 @@ static_assert( std::is_invocable_v<RangeEmptyT, int (&&)[1]>);
|
|||
static_assert( std::is_invocable_v<RangeEmptyT, int (&)[1]>);
|
||||
static_assert( std::is_invocable_v<RangeEmptyT, const int (&)[1]>);
|
||||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete[]>);
|
||||
static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete(&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeEmptyT, Incomplete(&&)[]>);
|
||||
|
||||
extern Incomplete array_of_incomplete[42];
|
||||
static_assert(!std::ranges::empty(array_of_incomplete));
|
||||
static_assert(!std::ranges::empty(std::move(array_of_incomplete)));
|
||||
static_assert(!std::ranges::empty(std::as_const(array_of_incomplete)));
|
||||
static_assert(!std::ranges::empty(static_cast<const Incomplete(&&)[42]>(array_of_incomplete)));
|
||||
|
||||
struct NonConstSizeAndEmpty {
|
||||
int size();
|
||||
bool empty();
|
||||
|
|
|
@ -25,6 +25,17 @@ static_assert( std::is_invocable_v<RangeSizeT, int[1]>);
|
|||
static_assert( std::is_invocable_v<RangeSizeT, int (&&)[1]>);
|
||||
static_assert( std::is_invocable_v<RangeSizeT, int (&)[1]>);
|
||||
|
||||
struct Incomplete;
|
||||
static_assert(!std::is_invocable_v<RangeSizeT, Incomplete[]>);
|
||||
static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&)[]>);
|
||||
static_assert(!std::is_invocable_v<RangeSizeT, Incomplete(&&)[]>);
|
||||
|
||||
extern Incomplete array_of_incomplete[42];
|
||||
static_assert(std::ranges::size(array_of_incomplete) == 42);
|
||||
static_assert(std::ranges::size(std::move(array_of_incomplete)) == 42);
|
||||
static_assert(std::ranges::size(std::as_const(array_of_incomplete)) == 42);
|
||||
static_assert(std::ranges::size(static_cast<const Incomplete(&&)[42]>(array_of_incomplete)) == 42);
|
||||
|
||||
static_assert(std::semiregular<std::remove_cv_t<RangeSizeT>>);
|
||||
|
||||
struct SizeMember {
|
||||
|
|
Loading…
Reference in New Issue