Skip to content

<algorithm>: investigate sharing code between std and ranges algorithms #1754

@CaseyCarter

Description

@CaseyCarter

There's an enormous amount of overlap in implementations of the "same" algorithm in namespaces std and std::ranges. For example, std::copy:

STL/stl/inc/xutility

Lines 4119 to 4146 in 2d890fd

template <class _InIt, class _OutIt>
_CONSTEXPR20 _OutIt _Copy_unchecked(_InIt _First, _InIt _Last, _OutIt _Dest) {
// copy [_First, _Last) to [_Dest, ...)
// note: _Copy_unchecked has callers other than the copy family
if constexpr (_Ptr_copy_cat<_InIt, _OutIt>::_Trivially_copyable) {
#ifdef __cpp_lib_is_constant_evaluated
if (!_STD is_constant_evaluated())
#endif // __cpp_lib_is_constant_evaluated
{
return _Copy_memmove(_First, _Last, _Dest);
}
}
for (; _First != _Last; ++_Dest, (void) ++_First) {
*_Dest = *_First;
}
return _Dest;
}
template <class _InIt, class _OutIt>
_CONSTEXPR20 _OutIt copy(_InIt _First, _InIt _Last, _OutIt _Dest) { // copy [_First, _Last) to [_Dest, ...)
_Adl_verify_range(_First, _Last);
const auto _UFirst = _Get_unwrapped(_First);
const auto _ULast = _Get_unwrapped(_Last);
const auto _UDest = _Get_unwrapped_n(_Dest, _Idl_distance<_InIt>(_UFirst, _ULast));
_Seek_wrapped(_Dest, _Copy_unchecked(_UFirst, _ULast, _UDest));
return _Dest;

vs. ranges::copy:

STL/stl/inc/algorithm

Lines 1507 to 1547 in 2d890fd

// VARIABLE ranges::copy
// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, weakly_incrementable _Out>
requires indirectly_copyable<_It, _Out>
_NODISCARD constexpr copy_result<_It, _Out> _Copy_unchecked(_It _First, const _Se _Last, _Out _Result) {
for (; _First != _Last; ++_First, (void) ++_Result) {
*_Result = *_First;
}
return {_STD move(_First), _STD move(_Result)};
}
// clang-format on
class _Copy_fn : private _Not_quite_object {
public:
using _Not_quite_object::_Not_quite_object;
// clang-format off
template <input_iterator _It, sentinel_for<_It> _Se, weakly_incrementable _Out>
requires indirectly_copyable<_It, _Out>
constexpr copy_result<_It, _Out> operator()(_It _First, _Se _Last, _Out _Result) const {
_Adl_verify_range(_First, _Last);
auto _UResult = _RANGES _Copy_unchecked(
_Get_unwrapped(_STD move(_First)), _Get_unwrapped(_STD move(_Last)), _STD move(_Result));
_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}
template <input_range _Rng, weakly_incrementable _Out>
requires indirectly_copyable<iterator_t<_Rng>, _Out>
constexpr copy_result<borrowed_iterator_t<_Rng>, _Out> operator()(_Rng&& _Range, _Out _Result) const {
auto _First = _RANGES begin(_Range);
auto _UResult =
_RANGES _Copy_unchecked(_Get_unwrapped(_STD move(_First)), _Uend(_Range), _STD move(_Result));
_Seek_wrapped(_First, _STD move(_UResult.in));
return {_STD move(_First), _STD move(_UResult.out)};
}
// clang-format on
};
inline constexpr _Copy_fn copy{_Not_quite_object::_Construct_tag{}};

There are substantial differences in the interface, but eventually they boil down to identical or nearly identical loops. It would be nice if we could reduce the total amount of code we must maintain by using a common back-end for such algorithms.

Note that in doing so we absolutely must not regress performance or codesize, and should not regress compile throughput or increase the complexity of the overall code.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementSomething can be improved

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions