decltype参数错误:Win32 API线程化无返回类型函数问题
问题:简化模板函数调用,实现
Start(ltest)而非Start<int, ltest>() 我最近在研究用Win32 API创建线程运行函数,想要实现更简洁的调用方式:直接用Start(ltest),而不是必须显式指定模板参数的Start<int, ltest>()。目前我写的代码遇到了编译错误,虽然有可行版本但不够简洁,希望能优化。
报错的代码实现
template <class R, R func() > unsigned int usualfunc() { func(); return 1; } template <class R> int Start(R(*func)()) { typedef decltype(&usualfunc<int, func>) D; // 此处报错:error 3556 'usualfunc': incorrect argument to 'decltype' D p = &usualfunc<R, func>; uintptr_t add = (uintptr_t)p; CreateThread(0, 0, (LPTHREAD_START_ROUTINE)add, 0, 0, 0); func(); return 1; } int main() { Start(ltest); // 期望的简洁调用方式 }
可行但繁琐的代码
这段代码能正常运行,但必须显式指定模板参数,不够简洁:
template <class R, R func() > unsigned int usualfunc() { func(); return 1; } template <class R,R func()> int Start() { typedef decltype(&usualfunc<int, func>) D; D p = &usualfunc<R, func>; uintptr_t add = (uintptr_t)p; CreateThread(0, 0, (LPTHREAD_START_ROUTINE)add, 0, 0, 0); func(); return 1; } int main() { Start<int,ltest>(); // 必须显式指定模板参数 }
注意:请勿建议将函数作为usualfunc的参数传入,我需要把它作为模板参数,因为CreateThread()无法直接传递函数作为参数。
解决方案
问题的核心在于:报错代码中func是Start函数的参数(函数指针变量),而模板非类型参数需要的是编译期常量,不能直接把运行时变量作为模板参数传给usualfunc。
我们可以利用C++17的auto模板非类型参数简化代码,同时通过一层包装函数实现自动推导,达成Start(ltest)的调用方式:
#include <windows.h> // 示例测试函数 int ltest() { // 这里写你的函数逻辑 return 0; } // 通用线程入口包装函数,接受任意无参函数作为模板参数 template <auto Func> unsigned int ThreadWrapper() { Func(); return 1; } // 真正的启动函数,使用auto模板参数接收编译期函数常量 template <auto Func> int StartImpl() { // 将包装函数地址转换为LPTHREAD_START_ROUTINE auto wrapperAddr = reinterpret_cast<uintptr_t>(&ThreadWrapper<Func>); CreateThread(nullptr, 0, reinterpret_cast<LPTHREAD_START_ROUTINE>(wrapperAddr), nullptr, 0, nullptr); Func(); // 同步调用,根据需求可删除 return 1; } // 外层包装函数,自动推导函数参数,实现简洁调用 template <typename FuncType> int Start(FuncType func) { // 将函数指针作为编译期模板参数传入StartImpl return StartImpl<func>(); } int main() { Start(ltest); // 现在可以直接这样调用了! return 0; }
关键说明
- auto模板非类型参数:C++17及以后支持用
auto作为模板非类型参数,让ThreadWrapper和StartImpl能接受任意无参函数作为编译期常量,无需手动指定返回类型。 - 外层包装函数:
Start(FuncType func)负责接收函数指针参数,再将其作为编译期模板参数传给StartImpl,让编译器自动推导函数类型,实现简洁调用。 - 规范类型转换:用
reinterpret_cast代替C风格强制转换,这是C++中更安全、规范的类型转换方式。
这样既满足了将函数作为模板参数的要求,又实现了期望的简洁调用,同时解决了编译错误。
内容的提问来源于stack exchange,提问作者dev65
相关产品推荐
相关产品推荐

