是否有与 std::forward 等效的移动或复制功能,可以在 std::ranges 中完美转发?

joz*_*yqk 5 c++ perfect-forwarding c++20 std-ranges

How can I combine the following two functions into one? Is there something similar to std::forward, but for ranges?

#include <ranges>
#include <vector>
#include <algorithm>

template<class RangeIn, class RangeOut>
void moveOrCopy(RangeIn& from, RangeOut& to)
{
    std::ranges::copy(from, std::back_inserter(to));
}

template<class RangeIn, class RangeOut>
    requires std::is_rvalue_reference_v<RangeIn&&>
void moveOrCopy(RangeIn&& from, RangeOut& to)
{
    std::ranges::move(from, std::back_inserter(to));
}

void test()
{
  std::vector<int> a, b;
  moveOrCopy(a, b); // copy
  moveOrCopy(std::move(a), b); // move
}
Run Code Online (Sandbox Code Playgroud)

There is std::ranges::forward_range, but that's related to forward_iterator, not perfect forwarding.


Handy tool with the above code: https://cppinsights.io/s/45c86608

Intuitive reference for C++ references: https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers

Wei*_*hou 7

If you have C++23, you can use std::ranges::transform with std::forward_like.

template<class RangeIn, class RangeOut>
void moveOrCopy2(RangeIn&& from, RangeOut& to)
{
    std::ranges::transform(from, std::back_inserter(to), [](auto&& arg) -> decltype(auto) {
        return std::forward_like<RangeIn>(arg);
    });
}
Run Code Online (Sandbox Code Playgroud)

Demo: https://godbolt.org/z/P81of9qav


Qui*_*mby 6

It is not overly clever but if constexpr can clean it up a little bit:

#include <ranges>
#include <vector>
#include <algorithm>

template<class RangeIn, class RangeOut>
void forward_range(RangeIn&& from, RangeOut& to)
{
    if constexpr(std::is_rvalue_reference_v<RangeIn&&>)
        std::ranges::move(from, std::back_inserter(to));
    else
        std::ranges::copy(from, std::back_inserter(to));
}

void test()
{
  std::vector<int> a, b;
  forward_range(a, b); // copy
  forward_range(std::move(a), b); // move
}
Run Code Online (Sandbox Code Playgroud)


T.C*_*.C. 2

从通用范围的值类别推断元素的生命周期属性是一个坏主意。例如:

  1. Astd::span<std::string>不应该将其元素移出,即使是span右值;跨度的值类别完全无关。
  2. 对于ranges::transform_view<std::span<std::string>, some_function>右值也是如此,即使它不是borrowed_range.
  3. 同样,对于 a 来说boost::iterator_range<std::string*>,即使这个遗留类型没有声明自己是 a view
  4. 对于 来说也是如此owning_view<boost::iterator_range<std::string*>>,尽管它是 的专业化owning_view

目前还没有任何概念或特征可以让人们可靠地检测何时可以安全地执行此操作。最好的办法是让调用者使用views::as_rvalueC++23 中的类似方法显式选择加入。