如何让std::from_range在合适时机自动移动元素?
问题:std::from_range处理右值范围时自动移动元素的惯用解决方案
请看以下代码:
#include <ranges> #include <memory> #include <list> #include <vector> template <typename T> struct A { std::vector<T> v; template <typename U> A(U &&input) : v(std::from_range, std::forward<U>(input)) {} }; int main() { A<std::unique_ptr<int>> a(std::list<std::unique_ptr<int>>{}); }
这段代码无法编译,因为std::from_range尝试复制std::unique_ptr<int>元素,而unique_ptr是不可复制类型。
我希望当源范围是右值且拥有元素时,能自动移动元素,请问正确且符合惯用写法的解决方案是什么?
我知道存在std::views::as_rvalue,但判断何时使用它需要满足两个条件:
- 输入范围必须是右值,且
- 它必须拥有元素(或许用
std::borrowed_range<U &> == false判断?不确定)
当然,我可以手动写出这些条件,但原本希望Ranges库有内置的解决方案来处理这个问题。
解决方案
C++23的std::from_range本身不会根据范围的值类别自动选择移动或复制行为,但我们可以通过条件性地将右值非借用范围转换为右值视图实现需求,这是符合惯用写法的方案:
#include <ranges> #include <memory> #include <list> #include <vector> #include <type_traits> template <typename T> struct A { std::vector<T> v; template <std::ranges::input_range U> A(U&& input) : v(std::from_range, std::conditional_t< std::is_rvalue_reference_v<U&&> && !std::ranges::borrowed_range<U>, decltype(std::views::as_rvalue(std::forward<U>(input))), U&& >(std::forward<U>(input))) {} }; int main() { // 右值范围:元素被移动,编译通过 A<std::unique_ptr<int>> a(std::list<std::unique_ptr<int>>{}); // 左值范围:元素被复制,符合预期 std::list<std::unique_ptr<int>> lst; A<std::unique_ptr<int>> b(lst); }
关键细节说明
- 条件判断逻辑:
std::is_rvalue_reference_v<U&&>:判断输入范围是否是右值(临时对象或被std::move的对象)!std::ranges::borrowed_range<U>:判断范围是否拥有自身的元素——借用范围(如视图)不持有元素,不需要移动,直接复制即可
- 行为控制:
- 当两个条件同时满足时,通过
std::views::as_rvalue将范围元素转为右值引用,std::from_range会调用元素的移动构造函数 - 否则直接转发原范围,保持默认的复制行为
- 当两个条件同时满足时,通过
- 现状说明:
C++23目前没有内置的"自动移动右值范围"的视图或构造选项,这种条件转发是当前最符合惯用风格的解决方案。
内容的提问来源于stack exchange,提问作者HolyBlackCat
相关产品推荐
相关产品推荐

