C++:基于传入Lambda返回值的模板特化实现问题
解决方案
C++14 实现
利用SFINAE(Substitution Failure Is Not An Error)特性,通过std::enable_if区分返回值为void和非void的情况,实现两个重载函数,让编译器自动匹配:
#include <boost/date_time/posix_time/posix_time.hpp> #include <type_traits> #include <utility> // 处理返回值为void的可调用对象 template <typename F, typename... Args, typename = std::enable_if_t<std::is_void<decltype(std::declval<F>()(std::declval<Args>()...))>::value>> static void measured_run(double& time, F f, Args&&... args) { auto start = boost::posix_time::microsec_clock::local_time(); f(std::forward<Args>(args)...); auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); } // 处理返回值非void的可调用对象 template <typename F, typename... Args, typename = std::enable_if_t<!std::is_void<decltype(std::declval<F>()(std::declval<Args>()...))>::value>> static auto measured_run(double& time, F f, Args&&... args) -> decltype(f(std::forward<Args>(args)...)) { auto start = boost::posix_time::microsec_clock::local_time(); auto result = f(std::forward<Args>(args)...); auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); return result; }
调用方式统一为:
double time = 0.0; measured_run(time, []() { return false; }); measured_run(time, []() { printf("void\n"); });
C++17 实现
C++17引入的if constexpr允许在编译时进行分支判断,无需拆分两个函数,将逻辑合并到一个模板中:
#include <boost/date_time/posix_time/posix_time.hpp> #include <type_traits> #include <utility> template <typename F, typename... Args> static auto measured_run(double& time, F f, Args&&... args) { auto start = boost::posix_time::microsec_clock::local_time(); using ReturnType = decltype(f(std::forward<Args>(args)...)); if constexpr (std::is_void_v<ReturnType>) { f(std::forward<Args>(args)...); auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); } else { ReturnType result = f(std::forward<Args>(args)...); auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); return result; } }
if constexpr会在编译时丢弃未选中的分支,避免void result这类非法代码被实例化,完美解决原代码的编译错误。
C++20 实现
C++20可以借助std::invoke_result_t简化返回类型推导,同时用std::invoke增强可调用对象的兼容性(支持成员函数、函数指针等更多类型):
#include <boost/date_time/posix_time/posix_time.hpp> #include <type_traits> #include <utility> #include <functional> // 用于std::invoke和std::invoke_result_t template <typename F, typename... Args> auto measured_run(double& time, F f, Args&&... args) { const auto start = boost::posix_time::microsec_clock::local_time(); if constexpr (std::is_void_v<std::invoke_result_t<F, Args...>>) { std::invoke(f, std::forward<Args>(args)...); const auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); } else { const auto result = std::invoke(f, std::forward<Args>(args)...); const auto end = boost::posix_time::microsec_clock::local_time(); time += (end - start).total_milliseconds(); return result; } }
原问题分析
你遇到的编译错误源于:当lambda返回void时,measured_run_ret_val会尝试定义auto result为void类型(C++不允许声明void类型的变量),同时在返回void的函数中使用return result也是非法的。通过上述分情况处理的方式,就能避免这些问题。
内容的提问来源于stack exchange,提问作者Chariphuk
相关产品推荐
相关产品推荐

