You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何std::set<std::unique_ptr<int>>结合std::views::as_rvalue转换失败?

问题:std::set<std::unique_ptr<int>>结合as_rvalue与to<vector>编译失败分析

我编写了一段代码,其中仅最后一个代码块无法运行。原以为这与std::unique_ptr仅可移动或std::set的键为const有关,但无法理解为何其他代码块可正常运行。

代码示例

namespace sr = std::ranges;
namespace sv = std::views;
int main() {
    {
        std::set<int> up_s{10,20};
        const auto up_vec = sv::as_rvalue(up_s) | sr::to<std::vector>();
        assert(up_vec.size() == 2);
    }
    {
        std::vector<std::unique_ptr<int>> up_d;
        up_d.emplace_back(std::make_unique<int>(10));
        up_d.emplace_back(std::make_unique<int>(20));
        const auto up_vec =  sv::as_rvalue(up_d) | sr::to<std::vector>();
        assert(up_vec.size() == 2);
    }    
    {
        std::set<std::unique_ptr<int>> up_d;
        up_d.emplace(std::make_unique<int>(10));
        up_d.emplace(std::make_unique<int>(20));
        // 取消注释后编译失败
        // const auto up_vec =  sv::as_rvalue(up_d) | sr::to<std::vector>();
        // assert(up_vec.size() == 2);
    }        
}

编译错误信息

/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/ranges:9354:25: 错误:静态断言失败
9354 | static_assert(input_range<range_reference_t<_Rg>>);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/ranges:9354:25: 注意:约束未满足

...

/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:499:13:
要求满足 'range<_Tp>' [其中 _Tp = const std::unique_ptr<int, std::default_delete>&&]
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:499:21:
在要求中使用 '_Tp& __t' [其中 _Tp = const std::unique_ptr<int, std::default_delete>&&]
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:501:22:
注意:所需表达式 'std::ranges::_Cpo::begin(__t)' 无效
501 | ranges::begin(__t);
| ~~~~~~~~~^
/opt/compiler-explorer/gcc-trunk-20240513/include/c++/15.0.0/bits/ranges_base.h:502:20:
注意:所需表达式 'std::ranges::_Cpo::end(__t)' 无效
502 | ranges::end(__t);

原因分析

核心问题出在GCC对std::ranges::to的模板推导逻辑上:

  • std::set的元素天生是const的,因此sv::as_rvalue(up_d)生成的range元素类型是const std::unique_ptr<int>&&。
  • 当使用sr::to<std::vector>()(未显式指定元素类型)时,编译器的模板推导错误地将该元素类型当成了range类型,尝试对const std::unique_ptr<int>&&调用begin()和end(),触发了input_range<range_reference_t<_Rg>>的静态断言——而unique_ptr显然不是range,因此断言失败。

对比前两个可正常运行的案例:

  1. std::set<int>案例:元素类型是const int&&,int不是range,编译器不会误判,可正常移动构造vector<int>。
  2. std::vector<std::unique_ptr<int>>案例:vector的元素非const,as_rvalue后元素类型是std::unique_ptr<int>&&,同样不会被误判为range,可正常移动构造vector<std::unique_ptr<int>>。

解决方案

只需显式指定std::ranges::to的目标容器元素类型,避免模板推导歧义:

const auto up_vec = sv::as_rvalue(up_d) | sr::to<std::vector<std::unique_ptr<int>>>();

或者通过std::views::transform显式移动元素,让推导逻辑更清晰:

const auto up_vec = sv::as_rvalue(up_d) 
                  | sv::transform([](auto&& ptr) { return std::move(ptr); }) 
                  | sr::to<std::vector>();

内容的提问来源于stack exchange,提问作者NoSenseEtAl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.23 20:43:17