struct _Pipe;
// The base class of every range adaptor closure.
+ //
+ // The derived class should define the optional static data member
+ // _S_has_simple_call_op to true if the behavior of this adaptor is
+ // independent of the constness/value category of the adaptor object.
struct _RangeAdaptorClosure
{
// range | adaptor is equivalent to adaptor(range).
// The static data member _Derived::_S_arity must contain the total number of
// arguments that the adaptor takes, and the class _Derived must introduce
// _RangeAdaptor::operator() into the class scope via a using-declaration.
+ //
+ // The optional static data member _Derived::_S_has_simple_extra_args should
+ // be defined to true if the behavior of this adaptor is independent of the
+ // constness/value category of the extra arguments.
template<typename _Derived>
struct _RangeAdaptor
{
}
};
+ // True if the range adaptor closure _Adaptor has a simple operator(), i.e.
+ // one that's not overloaded according to constness or value category of the
+ // _Adaptor object.
+ template<typename _Adaptor>
+ concept __closure_has_simple_call_op = _Adaptor::_S_has_simple_call_op;
+
+ // True if the behavior of the range adaptor non-closure _Adaptor is
+ // independent of the value category of its extra arguments.
+ template<typename _Adaptor>
+ concept __adaptor_has_simple_extra_args = _Adaptor::_S_has_simple_extra_args;
+
// A range adaptor closure that represents partial application of
// the range adaptor _Adaptor with arguments _Args.
template<typename _Adaptor, typename... _Args>
{ }
// Invoke _Adaptor with arguments __r, _M_args... according to the
- // value category of the range adaptor closure object.
+ // value category of this _Partial object.
template<typename _Range>
requires __adaptor_invocable<_Adaptor, _Range, const _Args&...>
constexpr auto
operator()(_Range&& __r) const && = delete;
};
+ // Partial specialization of the primary template for the case where the extra
+ // arguments of the adaptor can always be safely forwarded by const reference.
+ // This lets us get away with a single operator() overload, which makes
+ // overload resolution failure diagnostics more concise.
+ template<typename _Adaptor, typename... _Args>
+ requires __adaptor_has_simple_extra_args<_Adaptor>
+ struct _Partial<_Adaptor, _Args...> : _RangeAdaptorClosure
+ {
+ tuple<_Args...> _M_args;
+
+ constexpr
+ _Partial(_Args... __args)
+ : _M_args(std::move(__args)...)
+ { }
+
+ // Invoke _Adaptor with arguments __r, const _M_args&... regardless
+ // of the value category of this _Partial object.
+ template<typename _Range>
+ requires __adaptor_invocable<_Adaptor, _Range, const _Args&...>
+ constexpr auto
+ operator()(_Range&& __r) const
+ {
+ auto __forwarder = [&__r] (const auto&... __args) {
+ return _Adaptor{}(std::forward<_Range>(__r), __args...);
+ };
+ return std::apply(__forwarder, _M_args);
+ }
+
+ static constexpr bool _S_has_simple_call_op = true;
+ };
+
+ // A lightweight specialization of the above template for the common case
+ // where _Adaptor accepts a single extra argument.
+ template<typename _Adaptor, typename _Arg>
+ requires __adaptor_has_simple_extra_args<_Adaptor>
+ struct _Partial<_Adaptor, _Arg> : _RangeAdaptorClosure
+ {
+ _Arg _M_arg;
+
+ constexpr
+ _Partial(_Arg __arg)
+ : _M_arg(std::move(__arg))
+ { }
+
+ template<typename _Range>
+ requires __adaptor_invocable<_Adaptor, _Range, const _Arg&>
+ constexpr auto
+ operator()(_Range&& __r) const
+ { return _Adaptor{}(std::forward<_Range>(__r), _M_arg); }
+
+ static constexpr bool _S_has_simple_call_op = true;
+ };
+
template<typename _Lhs, typename _Rhs, typename _Range>
concept __pipe_invocable
= requires { std::declval<_Rhs>()(std::declval<_Lhs>()(std::declval<_Range>())); };
constexpr auto
operator()(_Range&& __r) const && = delete;
};
+
+ // A partial specialization of the above primary template for the case where
+ // both adaptor operands have a simple operator(). This in turn lets us
+ // implement composition using a single simple operator(), which makes
+ // overload resolution failure diagnostics more concise.
+ template<typename _Lhs, typename _Rhs>
+ requires __closure_has_simple_call_op<_Lhs>
+ && __closure_has_simple_call_op<_Rhs>
+ struct _Pipe<_Lhs, _Rhs> : _RangeAdaptorClosure
+ {
+ [[no_unique_address]] _Lhs _M_lhs;
+ [[no_unique_address]] _Rhs _M_rhs;
+
+ constexpr
+ _Pipe(_Lhs __lhs, _Rhs __rhs)
+ : _M_lhs(std::move(__lhs)), _M_rhs(std::move(__rhs))
+ { }
+
+ template<typename _Range>
+ requires __pipe_invocable<const _Lhs&, const _Rhs&, _Range>
+ constexpr auto
+ operator()(_Range&& __r) const
+ { return _M_rhs(_M_lhs(std::forward<_Range>(__r))); }
+
+ static constexpr bool _S_has_simple_call_op = true;
+ };
} // namespace views::__adaptor
template<range _Range> requires is_object_v<_Range>
else
return subrange{std::forward<_Range>(__r)};
}
+
+ static constexpr bool _S_has_simple_call_op = true;
};
inline constexpr _All all;
using _RangeAdaptor<_Filter>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _Filter filter;
using _RangeAdaptor<_Transform>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _Transform transform;
using _RangeAdaptor<_Take>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _Take take;
using _RangeAdaptor<_TakeWhile>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _TakeWhile take_while;
using _RangeAdaptor<_Drop>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _Drop drop;
using _RangeAdaptor<_DropWhile>::operator();
static constexpr int _S_arity = 2;
+ static constexpr bool _S_has_simple_extra_args = true;
};
inline constexpr _DropWhile drop_while;
// 3474. Nesting join_views is broken because of CTAD
return join_view<all_t<_Range>>{std::forward<_Range>(__r)};
}
+
+ static constexpr bool _S_has_simple_call_op = true;
};
inline constexpr _Join join;
using _RangeAdaptor<_Split>::operator();
static constexpr int _S_arity = 2;
+ // The second argument of views::split is _not_ simple -- it can be a
+ // non-view range, the value category of which affects whether the call is
+ // well-formed. So we must not define _S_has_simple_extra_args to true.
};
inline constexpr _Split split;
else
return common_view{std::forward<_Range>(__r)};
}
+
+ static constexpr bool _S_has_simple_call_op = true;
};
inline constexpr _Common common;
else
return reverse_view{std::forward<_Range>(__r)};
}
+
+ static constexpr bool _S_has_simple_call_op = true;
};
inline constexpr _Reverse reverse;
{
return elements_view<all_t<_Range>, _Nm>{std::forward<_Range>(__r)};
}
+
+ static constexpr bool _S_has_simple_call_op = true;
};
template<size_t _Nm>
--- /dev/null
+// 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 all multi-argument adaptors except for views::split are denoted
+ // to have simple extra arguments.
+ using views::__adaptor::__adaptor_has_simple_extra_args;
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::transform)>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::filter)>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::drop)>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::take)>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::take_while)>);
+ static_assert(__adaptor_has_simple_extra_args<decltype(views::drop_while)>);
+ static_assert(!__adaptor_has_simple_extra_args<decltype(views::split)>);
+
+ // 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;
+ // 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);
+
+ // Verify views::split is the exception.
+ auto a11 = views::split(' ');
+ static_assert(!__closure_has_simple_call_op<decltype(a11)>);
+ static_assert(!__closure_has_simple_call_op<decltype(a11 | a00)>);
+ static_assert(!__closure_has_simple_call_op<decltype(a00 | a11)>);
+}
+
+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];
+ auto badarg = nullptr;
+ views::transform(badarg)(x); // { dg-error "no match" }
+ views::filter(badarg)(x); // { dg-error "no match" }
+ views::take(badarg)(x); // { dg-error "no match" }
+ views::drop(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(badarg) | views::all)(x); // { dg-error "no match" }
+ (views::drop(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" }
+}
+
+// { dg-prune-output "in requirements" }