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

为何ranges::view_interface<T>::size要求移动构造函数?

自定义View中移动构造要求的来源解析

问题

我搞不懂自定义的vrange为啥会被要求实现移动构造函数,翻了forward_range和sized_sentinel的相关定义,都没找到对应的依据。

基础示例代码

#include <ranges>
#include <string>
#include <iostream>

class vrange: public std::ranges::view_interface<vrange>
{
    public:
        vrange(std::string &d): data(d){;};

        // 错误的移动构造声明:右值引用不该加const
        vrange(const vrange &&) = delete;

        auto begin() const noexcept { return data.begin(); };
        auto end() const noexcept { return data.end(); };

    private:
        std::string data;
};

int main(){
    std::string h("Hello world");
    vrange r(h);

    std::cout << r.size() << std::endl;
    for (const auto &i: r){
        std::cout << i;
        }
    std::cout << std::endl;
}

现象

两种方式可以让代码正常编译:

  • 移除std::cout << r.size() << std::endl;这一行调用
  • 把vrange的移动构造函数和赋值运算符设为默认(或者修正错误的移动构造声明并不删除它)

编译器报错信息(中文翻译)

/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h: 在实例化‘constexpr _Derived& std::ranges::view_interface<_Derived>::_M_derived() [with _Derived = vrange]’时:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:101:35:   要求从‘constexpr bool std::ranges::view_interface<_Derived>::empty() requires  forward_range<_Derived> [with _Derived = vrange]’中调用
w.cpp:25:12:   要求从这里调用
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: 错误:静态断言失败
   70 |         static_assert(view<_Derived>);
      |                       ^~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/bits/ranges_util.h:70:23: 注释:约束未满足
从/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/ranges:37包含的文件:
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:136:13:   要求满足‘constructible_from<_Tp, _Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:150:13:   要求满足‘move_constructible<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:247:13:   要求满足‘movable<_Tp>’ [with _Tp = vrange]
/usr/lib/gcc/x86_64-pc-linux-gnu/12/include/g++-v12/concepts:137:30: 注释:表达式‘is_constructible_v<_Tp, _Args ...> [with _Tp = vrange; _Args = {vrange}]’的值为‘false’
  137 |       = destructible<_Tp> && is_constructible_v<_Tp, _Args...>;

原因解析

  1. 要求来源:view概念
    你找错了地方——移动构造的要求不是来自forward_range或sized_sentinel,而是来自view概念本身。C++标准规定,view必须满足movable(可移动),而movable又要求类型是move_constructible(可移动构造)。

  2. 为何调用size()会触发检查
    view_interface提供的默认size()实现会间接调用到empty()方法,而empty()的实例化会触发view_interface内部的_M_derived()函数,这个函数里有static_assert(view<_Derived>);,强制验证派生类符合view概念。

  3. 代码里的错误点
    你写的vrange(const vrange &&)是完全错误的:右值引用不能加const,这个声明根本不是有效的移动构造函数,反而会删除编译器自动生成的默认移动构造,直接导致vrange不满足move_constructible要求。即使你本意是要禁用移动构造,正确的写法也是vrange(vrange&&) = delete;——但这样依然不符合view的要求,因为view必须支持移动操作。

解决方法

  • 如果你要写一个符合标准的view,就不要删除移动构造函数,要么用编译器默认生成的,要么显式声明为vrange(vrange&&) = default;
  • 如果你不需要view_interface提供的默认方法,或者不想遵守view的要求,可以不继承view_interface,自己实现需要的方法。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.14 00:45:57