C++中ADL调用std::ranges::size为何要求非const size函数?
std::ranges::size 两个删除重载的设计逻辑
这两个预置的删除size函数是C++20 ranges定制点对象(CPO)的标准拦截设计,核心目的是统一size操作的语义约束,避免ADL(参数依赖查找)意外匹配到语义不符合要求的重载,设计逻辑可拆分为两点说明:
1. 删除重载的核心作用
在通过ADL查找自定义非成员size时加入这两个删除候选,本质是做两层防护:
- 拦截泛型歧义:避免ADL意外匹配到关联命名空间中定义的过于泛化的
size模板(比如第三方库定义的template<class T> auto size(T&&)),这类泛型重载可以匹配任意类型,会完全破坏std::ranges::size的定制语义。加入这两个删除的模板重载后,无意义的泛型size匹配优先级不会高于标准预置的删除版本,会直接触发编译错误,避免语义被篡改。 - 强制语义合规:
size操作本身的语义约定是不修改范围对象、常数时间返回元素个数,这和标准库容器size() const成员函数的语义完全对齐。如果自定义的非成员size只能绑定非const左值引用,本质上暗示该操作可能修改范围,不符合ranges对size的语义要求,删除重载会在重载决议阶段拦截这类不符合约定的实现。
你遇到的加const就报错的问题,本质是重载决议的匹配规则导致的:当你定义size_t size(const Test&)时,该函数和标准预置的void size(const auto&) = delete模板对const引用参数的匹配优先级相同,若没有其他更优匹配路径,就会因为选中删除函数触发编译错误。
2. 为什么该约束仅对非成员size生效
这个约束只作用于非成员size的查找路径,核心原因是两类查找的逻辑完全不同:
- 成员
size的查找是直接检查t.size()表达式的合法性,查找范围仅包含当前类型及其基类的成员函数,不会向外查找其他命名空间的无关函数,不存在ADL带来的泛型匹配歧义,自然不需要额外插入删除重载做拦截。同时成员查找天然遵循const限定符规则,只有const成员函数才能被const对象、右值对象调用,和ranges的语义要求天然对齐。 - 非成员
size依赖ADL查找,查找范围会覆盖参数类型的所有关联命名空间,很容易匹配到无关的泛型size实现,必须靠删除重载做拦截,才能保证语义一致性。
内容的提问来源于stack exchange,提问作者zjyhjqs
相关产品推荐
相关产品推荐

