使用std::enable_if为何触发函数重载错误?
问题描述
我希望根据类的模板参数IS_DAILY定义两个重载的run成员函数,以下是我的C++14代码:
template <int VAL, typename D = std::chrono::seconds, bool IS_DAILY = false> class TimerJob { public: template <typename C, typename F, typename... Args, typename = std::enable_if_t<IS_DAILY>> void run(F C::*f, C* c, Args&&... args) { td_ = std::make_unique<std::thread>( [](F C::*f, C* c, Args... args){ do { c.f(args...); std::this_thread::sleep_for(D{VAL}); } while (true); }); } template <typename C, typename F, typename... Args, typename = std::enable_if_t<!IS_DAILY>> void run(F C::*f, C* c, Args&&... args) { td_ = std::make_unique<std::thread>( [](F C::*f, C* c, Args... args){ do { c.f(args...); // just an example to make a difference with the other run() // the real case is much complicated std::this_thread::sleep_for(D{VAL + 60}); } while (true); }); } private: std::unique_ptr<std::thread> td_; };
编译时出现如下错误:
<source>:27:8: error: 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)' cannot be overloaded with 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)' 27 | void run(F C::*f, C* c, Args&&... args) { | ^~~ <source>:15:8: note: previous declaration 'template<int VAL, class D, bool IS_DAILY> template<class C, class F, class ... Args, class> void TimerJob<VAL, D, IS_DAILY>::run(F C::*, C*, Args&& ...)' 15 | void run(F C::*f, C* c, Args&&... args) {
我感到困惑,std::enable_if不是这样使用的吗?
原因分析
C++模板重载决议规则中,默认模板参数不会参与函数模板的签名区分。你定义的两个run函数模板,除了最后一个enable_if相关的默认参数外,其余模板参数和函数参数完全一致,编译器会判定这两个是同一个函数模板,因此抛出重载冲突错误。
解决方法
方法1:调整enable_if的使用形式
将enable_if_t的结果作为带默认值的非类型模板参数,让它成为函数模板签名的一部分:
#include <memory> #include <thread> #include <chrono> #include <type_traits> template <int VAL, typename D = std::chrono::seconds, bool IS_DAILY = false> class TimerJob { public: template <typename C, typename F, typename... Args, std::enable_if_t<IS_DAILY, int> = 0> void run(F C::*f, C* c, Args&&... args) { td_ = std::make_unique<std::thread>( [f, c, args...]() mutable { do { (c->*f)(std::forward<Args>(args)...); // 修正成员函数指针调用语法 std::this_thread::sleep_for(D{VAL}); } while (true); }); } template <typename C, typename F, typename... Args, std::enable_if_t<!IS_DAILY, int> = 0> void run(F C::*f, C* c, Args&&... args) { td_ = std::make_unique<std::thread>( [f, c, args...]() mutable { do { (c->*f)(std::forward<Args>(args)...); std::this_thread::sleep_for(D{VAL + 60}); } while (true); }); } private: std::unique_ptr<std::thread> td_; };
方法2:标签分发(Tag Dispatch)
如果觉得enable_if的写法不够直观,可以用标签分发实现逻辑分离,避免重载冲突:
#include <memory> #include <thread> #include <chrono> #include <type_traits> template <int VAL, typename D = std::chrono::seconds, bool IS_DAILY = false> class TimerJob { private: // IS_DAILY为true时的实现 template <typename C, typename F, typename... Args> void run_impl(F C::*f, C* c, Args&&... args, std::true_type) { td_ = std::make_unique<std::thread>( [f, c, args...]() mutable { do { (c->*f)(std::forward<Args>(args)...); std::this_thread::sleep_for(D{VAL}); } while (true); }); } // IS_DAILY为false时的实现 template <typename C, typename F, typename... Args> void run_impl(F C::*f, C* c, Args&&... args, std::false_type) { td_ = std::make_unique<std::thread>( [f, c, args...]() mutable { do { (c->*f)(std::forward<Args>(args)...); std::this_thread::sleep_for(D{VAL + 60}); } while (true); }); } public: template <typename C, typename F, typename... Args> void run(F C::*f, C* c, Args&&... args) { // 根据IS_DAILY的值传递对应标签 run_impl(f, c, std::forward<Args>(args)..., std::integral_constant<bool, IS_DAILY>{}); } private: std::unique_ptr<std::thread> td_; };
补充:原代码的其他问题修正
除了重载冲突,原代码还有两处错误:
- 成员函数指针调用语法错误:必须使用
(c->*f)(args...)而非c.f(args...) - Lambda捕获方式问题:直接捕获参数而非依赖模板参数,避免生命周期和类型推导问题
内容的提问来源于stack exchange,提问作者Yves
相关产品推荐
相关产品推荐

