The libstdc++ implementation of std::ranges::ref_view, following the C++ standard, includes the following code:
template<range _Range> requires is_object_v<_Range>
class ref_view : public view_interface<ref_view<_Range>>
{
private:
_Range* _M_r;
static void _S_fun(_Range&); // not defined
static void _S_fun(_Range&&) = delete;
public:
template<__detail::__different_from<ref_view> _Tp>
requires convertible_to<_Tp, _Range&>
&& requires { _S_fun(declval<_Tp>()); }
constexpr
ref_view(_Tp&& __t)
noexcept(noexcept(static_cast<_Range&>(std::declval<_Tp>())))
: _M_r(std::__addressof(static_cast<_Range&>(std::forward<_Tp>(__t))))
{ }
};
There are three constraints on the constructor:
__detail::__different_from<ref_view>convertible_to<_Tp, _Range&>requires { _S_fun(declval<_Tp>()); }
My questions are:
- Is Constraint 2 stricter than Constraint 1, and is Constraint 3 stricter than Constraint 2? In other words, could we keep only Constraint 3 and remove the other two without changing the behavior?
- Since
std::is_object_v<_Range>ensures_Rangeis an object type, is the forwarding reference_Tp&&necessary? Would the following simpler non-template constructor suffice?
template<range _Range> requires is_object_v<_Range>
class ref_view : public view_interface<ref_view<_Range>>
{
private:
_Range* _M_r;
public:
constexpr
ref_view(_Range& __t)
noexcept
: _M_r(std::__addressof(__t))
{ }
};
I tried three AI models (Gemini, Grok, and Claude), and their answers contain various mistakes. For example, they incorrectly think that using non-template constructor would restrict the constructor to exact matches, preventing polymorphic use.
convertivle_toallows same types, constraint 3 seems indeed stricter than constraint 2 (and no subsumption seems involved)ref_view(_Range&&) = delete;for types with multiple conversion operator.__different_fromis needed to not compete with ref_view's own copy and move constructors.