
The _S_has_simple_extra_args mechanism is used to simplify forwarding of range adaptor's extra arguments when perfect forwarding call wrapper semantics isn't required for correctness, on a per-adaptor basis. Both views::take and views::drop are flagged as such, but it turns out perfect forwarding semantics are needed for these adaptors in some contrived cases, e.g. when their extra argument is a move-only class that's implicitly convertible to an integral type. To fix this, we could just clear the flag for views::take/drop as with views::split, but that'd come at the cost of acceptable diagnostics for ill-formed uses of these adaptors (see PR100577). This patch instead allows adaptors to parameterize their _S_has_simple_extra_args flag according the types of the captured extra arguments, so that we could conditionally disable perfect forwarding semantics only when the types of the extra arguments permit it. We then use this finer-grained mechanism to safely disable perfect forwarding semantics for views::take/drop when the extra argument is integer-like, rather than incorrectly always disabling it. Similarly, for views::split, rather than always enabling perfect forwarding semantics we now safely disable it when the extra argument is a scalar or a view, and recover good diagnostics for these common cases. PR libstdc++/100940 libstdc++-v3/ChangeLog: * include/std/ranges (__adaptor::_RangeAdaptor): Document the template form of _S_has_simple_extra_args. (__adaptor::__adaptor_has_simple_extra_args): Add _Args template parameter pack. Try to treat _S_has_simple_extra_args as a variable template parameterized by _Args. (__adaptor::_Partial): Pass _Arg/_Args to the constraint __adaptor_has_simple_extra_args. (views::_Take::_S_has_simple_extra_args): Templatize according to the type of the extra argument. (views::_Drop::_S_has_simple_extra_args): Likewise. (views::_Split::_S_has_simple_extra_args): Define. * testsuite/std/ranges/adaptors/100577.cc (test01, test02): Adjust after changes to _S_has_simple_extra_args mechanism. (test03): Define.
116 lines
4.9 KiB
C++
116 lines
4.9 KiB
C++
// Copyright (C) 2021 Free Software Foundation, Inc.
|
|
//
|
|
// This file is part of the GNU ISO C++ Library. This library is free
|
|
// software; you can redistribute it and/or modify it under the
|
|
// terms of the GNU General Public License as published by the
|
|
// Free Software Foundation; either version 3, or (at your option)
|
|
// any later version.
|
|
|
|
// This library is distributed in the hope that it will be useful,
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
// GNU General Public License for more details.
|
|
|
|
// You should have received a copy of the GNU General Public License along
|
|
// with this library; see the file COPYING3. If not see
|
|
// <http://www.gnu.org/licenses/>.
|
|
|
|
// { dg-options "-std=gnu++2a" }
|
|
// { dg-do compile { target c++2a } }
|
|
|
|
// PR libstdc++/100577
|
|
|
|
#include <ranges>
|
|
|
|
namespace ranges = std::ranges;
|
|
namespace views = std::ranges::views;
|
|
|
|
void
|
|
test01()
|
|
{
|
|
// Verify adaptors are deemed to have simple extra arguments when appropriate.
|
|
using views::__adaptor::__adaptor_has_simple_extra_args;
|
|
using std::identity;
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::transform), identity>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::filter), identity>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::drop), int>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::take), int>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while), identity>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while), identity>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::split), std::string_view>);
|
|
static_assert(__adaptor_has_simple_extra_args<decltype(views::split), char>);
|
|
static_assert(!__adaptor_has_simple_extra_args<decltype(views::split), std::string>);
|
|
|
|
// Verify all adaptor closures except for views::split(pattern) have a simple
|
|
// operator().
|
|
using views::__adaptor::__closure_has_simple_call_op;
|
|
__closure_has_simple_call_op auto a00 = views::all;
|
|
__closure_has_simple_call_op auto a01 = views::transform(std::identity{});
|
|
__closure_has_simple_call_op auto a02 = views::filter(std::identity{});
|
|
__closure_has_simple_call_op auto a03 = views::drop(42);
|
|
__closure_has_simple_call_op auto a04 = views::take(42);
|
|
__closure_has_simple_call_op auto a05 = views::take_while(std::identity{});
|
|
__closure_has_simple_call_op auto a06 = views::drop_while(std::identity{});
|
|
__closure_has_simple_call_op auto a07 = views::join;
|
|
__closure_has_simple_call_op auto a08 = views::common;
|
|
__closure_has_simple_call_op auto a09 = views::reverse;
|
|
__closure_has_simple_call_op auto a10 = views::keys;
|
|
__closure_has_simple_call_op auto a11 = views::split(' ');
|
|
// Verify composition of simple closures is simple.
|
|
__closure_has_simple_call_op auto b
|
|
= (a00 | a01) | (a02 | a03) | (a04 | a05 | a06) | (a07 | a08 | a09 | a10) | a11;
|
|
|
|
// Verify views::split(non_view_range) is an exception.
|
|
extern std::string s;
|
|
auto a12 = views::split(s);
|
|
static_assert(!__closure_has_simple_call_op<decltype(a12)>);
|
|
static_assert(!__closure_has_simple_call_op<decltype(a12 | a00)>);
|
|
static_assert(!__closure_has_simple_call_op<decltype(a00 | a12)>);
|
|
}
|
|
|
|
void
|
|
test02()
|
|
{
|
|
// Range adaptor closures with a simple operator() aren't implemented using a
|
|
// fallback deleted overload, so when a call is ill-formed overload resolution
|
|
// fails.
|
|
extern int x[10];
|
|
struct { } badarg;
|
|
views::transform(badarg)(x); // { dg-error "no match" }
|
|
views::filter(badarg)(x); // { dg-error "no match" }
|
|
views::take_while(badarg)(x); // { dg-error "no match" }
|
|
views::drop_while(badarg)(x); // { dg-error "no match" }
|
|
|
|
(views::transform(badarg) | views::all)(x); // { dg-error "no match" }
|
|
(views::filter(badarg) | views::all)(x); // { dg-error "no match" }
|
|
(views::take_while(badarg) | views::all)(x); // { dg-error "no match" }
|
|
(views::drop_while(badarg) | views::all)(x); // { dg-error "no match" }
|
|
|
|
// In practice, range adaptor closures with non-simple operator() are
|
|
// implemented using a fallback deleted overload, so when a call is
|
|
// ill-formed overload resolution succeeds but selects the deleted overload
|
|
// (but only when the closure is invoked as an rvalue).
|
|
views::split(badarg)(x); // { dg-error "deleted function" }
|
|
(views::split(badarg) | views::all)(x); // { dg-error "deleted function" }
|
|
auto a0 = views::split(badarg);
|
|
a0(x); // { dg-error "no match" };
|
|
auto a1 = a0 | views::all;
|
|
a1(x); // { dg-error "no match" }
|
|
|
|
views::take(badarg)(x); // { dg-error "deleted" }
|
|
views::drop(badarg)(x); // { dg-error "deleted" }
|
|
(views::take(badarg) | views::all)(x); // { dg-error "deleted" }
|
|
(views::drop(badarg) | views::all)(x); // { dg-error "deleted" }
|
|
}
|
|
|
|
void
|
|
test03()
|
|
{
|
|
// PR libstdc++/100940
|
|
extern int x[10];
|
|
struct S { operator int() && { return 5; }; };
|
|
x | std::views::take(S{});
|
|
x | std::views::drop(S{});
|
|
}
|
|
|
|
// { dg-prune-output "in requirements" }
|