如何让std::function强制要求参数为引用,避免值传递错误?
解决方案
针对你遇到的std::function隐式接受值参数lambda导致不符合预期的问题,以下是具体解决方法:
问题1:让f1编译失败,强制参数为int引用
std::function允许将引用参数隐式绑定到值类型参数上,这是f1能编译但修改无效的原因。我们可以自定义一个严格的包装类替代std::function,在构造时检查可调用对象的参数是否为引用类型:
#include <functional> #include <type_traits> #include <tuple> #include <utility> template<typename Sig> class strict_function; template<typename Ret, typename... Args> class strict_function<Ret(Args...)> { private: std::function<Ret(Args...)> func_; // 检查非泛型lambda的参数是否为引用 template<typename F, std::size_t... I> static constexpr bool check_non_generic(F&&, std::index_sequence<I...>) { using FuncType = decltype(&F::operator()); using ArgsTuple = typename std::function<FuncType>::argument_type; return (... && std::is_reference_v<std::tuple_element_t<I, ArgsTuple>>); } // 检查泛型lambda的调用是否匹配引用参数 template<typename F> static constexpr bool check_generic(F&& f) { if constexpr (std::is_invocable_v<F, Args...>) { return (... && std::is_reference_v<decltype(std::declval<Args>())>); } return false; } template<typename F> static constexpr bool check_args(F&& f) { if constexpr (std::is_class_v<std::remove_reference_t<F>>) { using OpType = decltype(&std::remove_reference_t<F>::operator()); if constexpr (std::is_member_function_pointer_v<OpType>) { // 非泛型lambda return check_non_generic(std::forward<F>(f), std::make_index_sequence<sizeof...(Args)>{}); } else { // 泛型lambda return check_generic(std::forward<F>(f)); } } return false; } public: template<typename F, typename = std::enable_if_t<check_args(F{})>> strict_function(F&& f) : func_(std::forward<F>(f)) {} Ret operator()(Args... args) { return func_(std::forward<Args>(args)...); } };
将你的类型定义替换为:
using viref_func = strict_function<void(int& intref)>;
此时f1 = [](int foo) { ++foo; }会直接编译失败,因为参数不是引用类型,不符合检查规则。
问题2:让f2的auto推导为int引用,或编译失败
上面的strict_function已经包含了对泛型lambda的检查:当lambda写为[](auto foo)时,推导的foo是值类型,用int&调用虽然能隐式转换,但会被检查逻辑拒绝,直接编译失败;只有当用户写成[](auto& foo)时,推导的foo是int&,符合要求才能通过编译。
额外说明
这个自定义的strict_function完全兼容原std::function的用法,仅在构造阶段增加了参数类型检查,不会给使用者带来额外负担——只需替换原有的std::function类型定义即可,出错时编译器会给出明确提示,避免隐式转换带来的排查难题。
内容的提问来源于stack exchange,提问作者oreubens
相关产品推荐
相关产品推荐

