如何用C++20 Concepts指定函数返回值必须可迭代?
规范定义Splittable概念的C++实现方式
你最初尝试在requires子句中直接写for循环是行不通的,因为concept的requires表达式只接受合法的表达式语句,for循环不属于这个范畴。想要定义“可将容器拆分为多个片段”的概念,核心是要验证Adapter::Split的返回值是可迭代的范围,且范围中的元素符合你的类型要求,而非绑定到具体的容器类型。
推荐的规范写法
利用C++20的<ranges>库提供的概念来实现,既灵活又不局限于特定返回类型:
#include <ranges> #include <concepts> // 假设SplitCriterion是全局或已定义的类型 template <class Adapter, class Container, class SplitCriterion = SplitCriterion> concept Splittable = requires(const Container& shard, const SplitCriterion& criterion) { // 要求Split的返回值是一个输入范围(满足可迭代的基本要求) { Adapter::Split(shard, criterion) } -> std::ranges::input_range; // 要求范围中的元素可以转换为const Container&(或你需要的Collection类型) requires std::convertible_to< std::ranges::range_value_t<decltype(Adapter::Split(shard, criterion))>, const Container& >; };
如果需要更紧凑的表达,也可以用嵌套的requires子句直接验证迭代行为:
#include <ranges> #include <concepts> template <class Adapter, class Container, class SplitCriterion> concept Splittable = requires(const Container& shard, const SplitCriterion& criterion) { requires requires(auto&& split_result = Adapter::Split(shard, criterion)) { // 验证范围的首元素可解引用,且类型符合要求 { *std::ranges::begin(split_result) } -> std::convertible_to<const Container&>; // 验证范围有合法的end迭代器 std::ranges::end(split_result); }; };
这种写法的优势
- 完全解耦返回类型:不管
Split返回的是std::vector、absl::Span、自定义的生成器范围还是其他符合range概念的类型,只要能迭代出符合要求的元素,就能满足concept。 - 灵活的类型约束:如果你的场景允许返回
Collection的派生类实例,只需要把std::convertible_to<const Container&>换成std::derived_from<Collection>(或对应基类),就能支持派生类型的元素。
对比你之前的两种写法
- 绑定
std::vector的写法过于僵化,无法适配返回其他可迭代类型的场景,还会继承vector对元素的额外要求(比如可拷贝构造等); - 用
absl::Span的写法虽然灵活一些,但span依赖连续内存布局,且无法直接支持返回派生类的span(因为absl::Span<const Derived>不能隐式转换为absl::Span<const Collection>),限制了扩展性。
内容的提问来源于stack exchange,提问作者gct
相关产品推荐
相关产品推荐

