自定义接受谓词的函数:用右值引用传参是否正确?
自定义元组for_each模板中谓词参数的传递方式分析
先看问题里的自定义元组遍历模板:
template <typename... Args, typename Func, std::size_t... index> constexpr void for_each(const std::tuple<Args...>& t, Func&& f, std::index_sequence<index...>) { (f(std::get<index>(t)), ...); } template <typename... Args, typename Func> constexpr void for_each(const std::tuple<Args...>& t, Func&& f) { for_each(t, f, std::index_sequence_for<Args...>{}); }
以及示例用法:
auto t = std::make_tuple(5, std::string("")); for_each(t, [](auto& field) { std::cout << field << std::endl; });
一、用Func&&传递谓词是否正确?
这种写法方向正确,但有一处细节需要修正:
Func&&在这里是万能引用——当模板参数Func处于推导上下文时,右值引用会变成万能引用,既能绑定左值也能绑定右值,可以接受各种类型的谓词:左值lambda、右值临时lambda、函数指针等,是C++实现完美转发的标准写法。
但当前代码的内层调用存在问题:外层for_each传递f给内层时,没有保留原有的值类别,会导致右值谓词被退化为左值传递。正确写法需要用std::forward<Func>(f)来实现完美转发:
template <typename... Args, typename Func> constexpr void for_each(const std::tuple<Args...>& t, Func&& f) { for_each(t, std::forward<Func>(f), std::index_sequence_for<Args...>{}); }
修正后,模板既能适配左值/右值谓词,避免不必要的拷贝或移动,还支持带mutable修饰、需要修改自身状态的lambda。
二、是否应该改用const Func&传递?
换成const Func&确实能避免谓词拷贝,但会带来明显限制:
- 谓词必须支持const调用:比如lambda的
operator()必须是const的(默认lambda满足,但加mutable后会变成非const,此时const Func&无法调用)。 - 兼容性受限:无法适配需要移动语义的临时谓词,虽然多数场景下谓词都是轻量对象(比如lambda),移动成本可忽略,但通用性不如万能引用写法。
总结
- 若仅需处理无状态或const状态的谓词,
const Func&可行,但通用性不足。 - 更推荐万能引用+完美转发的写法(即修正后的
Func&&版本),它能适配所有类型的谓词,是C++标准库类似接口的通用实现方式。
内容的提问来源于stack exchange,提问作者Alexey Starinsky
相关产品推荐
相关产品推荐

