使用std::bind绑定成员函数初始化timer时编译报错求助
类成员中用std::bind绑定成员函数初始化timer模板类编译失败
尝试在类成员变量中初始化接受可调用对象的timer模板实例,通过std::bind绑定类成员函数,但编译报错,代码及GCC 12.2报错信息如下:
原代码
#include <functional> #include <type_traits> #include <cstdio> template <typename Callable> struct timer { template <typename = std::enable_if_t<std::is_invocable_v<Callable>, void>> timer(Callable func) : func_ {func} { } void invoke() const { func_(); } Callable func_; }; struct some_struct { void do_something() { timer_.invoke(); } void request() { printf("Hello\n"); } const timer<decltype(std::bind(&some_struct::request, some_struct*))> timer_ { std::bind(&some_struct::request, this) }; }; int main() { some_struct obj; obj.do_something(); }
GCC 12.2报错信息
<source>:31:70: error: expected primary-expression before '*' token 31 | const timer<decltype(std::bind(&some_struct::request, some_struct*))> timer_ { std::bind(&some_struct::request, this) }; | ^ <source>:31:71: error: expected primary-expression before ')' token 31 | const timer<decltype(std::bind(&some_struct::request, some_struct*))> timer_ { std::bind(&some_struct::request, this) }; | ^ <source>:31:73: error: template argument 1 is invalid 31 | const timer<decltype(std::bind(&some_struct::request, some_struct*))> timer_ { std::bind(&some_struct::request, this) }; | ^ <source>:31:123: error: cannot convert '<brace-enclosed initializer list>' to 'const int' in initialization 31 | const timer<decltype(std::bind(&some_struct::request, some_struct*))> timer_ { std::bind(&some_struct::request, this) }; | ^ <source>: In member function 'void some_struct::do_something()': <source>:24:16: error: request for member 'invoke' in '((some_struct*)this)->some_struct::timer_', which is of non-class type 'const int' 24 | timer_.invoke(); | ^~~~~~
问题分析
核心错误在timer_的类型声明中:decltype(std::bind(&some_struct::request, some_struct*))写法完全错误。std::bind的参数需要是具体的表达式(比如对象指针、实例),而some_struct*是类型,不是合法的表达式,编译器无法解析,导致模板参数无效,进而错误地将timer_推断为const int类型,引发后续一系列错误。
修复方案
方案一:改用std::function统一可调用类型
简化timer结构,使用std::function<void()>作为成员类型,无需在声明时指定bind的具体返回类型,代码更简洁:
#include <functional> #include <cstdio> struct timer { timer(std::function<void()> func) : func_ {std::move(func)} { } void invoke() const { func_(); } std::function<void()> func_; }; struct some_struct { void do_something() { timer_.invoke(); } void request() { printf("Hello\n"); } const timer timer_ { std::bind(&some_struct::request, this) }; }; int main() { some_struct obj; obj.do_something(); }
方案二:正确推导bind的返回类型
若坚持使用模板timer,可通过std::declval生成合法的表达式来推导std::bind的返回类型,解决类未完全定义的问题:
#include <functional> #include <type_traits> #include <cstdio> template <typename Callable> struct timer { template <typename = std::enable_if_t<std::is_invocable_v<Callable>, void>> timer(Callable func) : func_ {func} { } void invoke() const { func_(); } Callable func_; }; struct some_struct { void do_something() { timer_.invoke(); } void request() { printf("Hello\n"); } // 用std::declval生成some_struct*类型的表达式,用于推导bind返回类型 const timer<decltype(std::bind(&some_struct::request, std::declval<some_struct*>()))> timer_ { std::bind(&some_struct::request, this) }; }; int main() { some_struct obj; obj.do_something(); }
方案对比
- 方案一:
std::function可以兼容各种可调用对象(lambda、bind、函数指针等),代码简洁,适合大多数场景,仅存在极轻微的性能开销。 - 方案二:保留模板的类型推导,避免
std::function的开销,适合对性能敏感的场景,但写法稍复杂。
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

