为何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...>;
原因解析
要求来源:
view概念
你找错了地方——移动构造的要求不是来自forward_range或sized_sentinel,而是来自view概念本身。C++标准规定,view必须满足movable(可移动),而movable又要求类型是move_constructible(可移动构造)。为何调用
size()会触发检查view_interface提供的默认size()实现会间接调用到empty()方法,而empty()的实例化会触发view_interface内部的_M_derived()函数,这个函数里有static_assert(view<_Derived>);,强制验证派生类符合view概念。代码里的错误点
你写的vrange(const vrange &&)是完全错误的:右值引用不能加const,这个声明根本不是有效的移动构造函数,反而会删除编译器自动生成的默认移动构造,直接导致vrange不满足move_constructible要求。即使你本意是要禁用移动构造,正确的写法也是vrange(vrange&&) = delete;——但这样依然不符合view的要求,因为view必须支持移动操作。
解决方法
- 如果你要写一个符合标准的view,就不要删除移动构造函数,要么用编译器默认生成的,要么显式声明为
vrange(vrange&&) = default; - 如果你不需要
view_interface提供的默认方法,或者不想遵守view的要求,可以不继承view_interface,自己实现需要的方法。
内容的提问来源于stack exchange,提问作者serafean
相关产品推荐
相关产品推荐

