能否仅从范围参数推导C++输出范围的值类型?
问题:自定义范围函数如何自动推导值类型?
观察到std::ranges::fill和std::ranges::generate这类C标准算法可借助额外参数推导输出范围的值类型,例如ranges::fill(v, 10)可通过第二个参数推导出值类型T。但定义类似函数并移除第二个参数后,C无法自动推导值类型,示例代码如下:
template<typename T, std::output_range<const T&> R> void ones(R && r) { for (T & value : r) { value = 1; } }
调用时若不显式指定值类型则无法编译:
std::array<int, 3> a; // ones(a); // 无法编译 ones<int>(a); // 冗余!
编译注释代码会得到如下错误:
/home/josiest/sandbox/cpp/template-template-range/sketch.cpp: 在函数‘int main()’中: /home/josiest/sandbox/cpp/template-template-range/sketch.cpp:19:9: 错误:找不到匹配的函数调用‘ones(std::array<int, 3>&)’ 19 | ones(a); | ~~~~^~~ /home/josiest/sandbox/cpp/template-template-range/sketch.cpp:9:6: 注意:候选函数:‘template<class T, class R> requires output_range<R, T> void ones(R&&)’ 9 | void ones(R && r) | ^~~~ /home/josiest/sandbox/cpp/template-template-range/sketch.cpp:9:6: 注意: 模板参数推导/替换失败: /home/josiest/sandbox/cpp/template-template-range/sketch.cpp:19:9: 注意: 无法推导模板参数‘T’ 19 | ones(a); | ~~~~^~~
解决方案:从范围本身提取值类型
核心问题是原模板的参数顺序和约束写法错误——T无法从R直接推导,因为output_range<const T&> R的约束中T是未知的。要实现自动推导,需从范围R中提取其值类型,而非将T作为独立模板参数。
方法1:用std::ranges::range_value_t提取值类型
修改模板定义,移除独立的T参数,直接从R获取值类型:
template<std::output_range<R> R> void ones(R && r) { using T = std::ranges::range_value_t<R>; for (T & value : r) { value = 1; } }
若需要更严谨的约束,确保范围能接受T类型赋值:
template<typename R> requires std::output_range<R, std::ranges::range_value_t<R>> void ones(R && r) { using T = std::ranges::range_value_t<R>; for (T & value : r) { value = 1; } }
此时调用ones(a)时,编译器会自动从std::array<int,3>推导出T=int,无需显式指定。
方法2:用auto简化循环变量
若无需显式命名T,可直接在循环中使用auto&,进一步简化代码:
template<std::output_range<R> R> void ones(R && r) { for (auto & value : r) { value = 1; } }
这种写法中,编译器会自动推导循环变量类型为范围元素的引用类型,完全不需要手动指定T。
标准库算法的差异
像std::ranges::fill的签名为:
template<std::output_range<const T&> R> constexpr void fill(R&& r, const T& value);
这里T可通过第二个参数value直接推导,因此无需从范围提取值类型。而你的场景没有额外参数,必须从范围本身获取值类型。
内容的提问来源于stack exchange,提问作者Josie Thompson
相关产品推荐
相关产品推荐

