GCC下类作用域内模板显式特化报错的优雅解决方案问询
问题
在类作用域内尝试为默认初始化场景将类型擦除函数特化为empty_t类型时,触发编译错误:error: explicit specialization in non-namespace scope。已知GCC不允许类内部的全模板特化,需要将特化代码移到命名空间作用域,但类模板参数需要转发给外部函数,且无法针对empty_t对含Args...的函数模板做偏特化,求优雅的解决方式。
代码示例
#include <utility> #include <cstdio> template <typename... Ts> void myfunc(Ts... args) { printf("Do something!\n"); } template <typename R, typename... Args> struct entity; template <typename R, typename... Args> struct entity<R(Args...)> { using fn_invoke_t = R(*)(void*); struct empty_t{}; template <typename T> static R fn_invoke(Args&&... args) { return myfunc(args...); } template <> static R fn_invoke<empty_t>(Args&&... args) { return myfunc(args...); } entity() : fn_invoke_(reinterpret_cast<fn_invoke_t>(fn_invoke<empty_t>)) { } template <typename T> entity(T obj) : fn_invoke_( reinterpret_cast<fn_invoke_t>(fn_invoke<T>) ) { } fn_invoke_t fn_invoke_; }; int main() { entity<void()> hello = [](){ printf("Hello World!\n"); }; // hello(); }
错误信息
<source>:27:15: error: explicit specialization in non-namespace scope 'struct entity<R(Args ...)>' 27 | template <> | ^ <source>:28:14: error: template-id 'fn_invoke<entity<R(Args ...)>::empty_t>' in declaration of primary template 28 | static R fn_invoke<empty_t>(Args&&... args) {
解决方案
方法1:函数重载替代模板特化
类内部不允许全特化,但可通过重载实现等效逻辑。将fn_invoke拆分为通用模板版本和针对empty_t的非模板重载,用lambda包装调用以匹配函数签名:
#include <utility> #include <cstdio> template <typename... Ts> void myfunc(Ts... args) { printf("Do something!\n"); } template <typename R, typename... Args> struct entity; template <typename R, typename... Args> struct entity<R(Args...)> { using fn_invoke_t = R(*)(void*); struct empty_t{}; // 通用模板版本 template <typename T> static R fn_invoke(Args&&... args) { return myfunc(args...); } // 针对empty_t的重载 static R fn_invoke(empty_t, Args&&... args) { return myfunc(args...); } entity() : fn_invoke_(reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return fn_invoke(empty_t{}); } )) { } template <typename T> entity(T obj) : fn_invoke_( reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return fn_invoke<T>(); } )) { } fn_invoke_t fn_invoke_; }; int main() { entity<void()> hello = [](){ printf("Hello World!\n"); }; }
方法2:外部辅助类转发参数+命名空间特化
将模板函数移到外部辅助类,通过辅助类转发原类的模板参数,再在命名空间作用域完成empty_t的全特化:
#include <utility> #include <cstdio> template <typename... Ts> void myfunc(Ts... args) { printf("Do something!\n"); } // 辅助类,传递entity的模板参数 template <typename Sig> struct entity_invoke_helper; template <typename R, typename... Args> struct entity_invoke_helper<R(Args...)> { using empty_t = struct {}; template <typename T> static R fn_invoke(Args&&... args) { return myfunc(args...); } }; // 针对empty_t的全特化,放在命名空间作用域 template <typename R, typename... Args> template <> R entity_invoke_helper<R(Args...)>::fn_invoke<empty_t>(Args&&... args) { return myfunc(args...); } template <typename R, typename... Args> struct entity; template <typename R, typename... Args> struct entity<R(Args...)> { using helper = entity_invoke_helper<R(Args...)>; using fn_invoke_t = R(*)(void*); using empty_t = typename helper::empty_t; entity() : fn_invoke_(reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return helper::template fn_invoke<empty_t>(); } )) { } template <typename T> entity(T obj) : fn_invoke_( reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return helper::template fn_invoke<T>(); } )) { } fn_invoke_t fn_invoke_; }; int main() { entity<void()> hello = [](){ printf("Hello World!\n"); }; }
方法3:C++17 constexpr if 编译期分支
如果使用C++17及以上标准,可直接在模板函数内部用constexpr if判断模板参数是否为empty_t,避免特化或重载:
#include <utility> #include <cstdio> #include <type_traits> template <typename... Ts> void myfunc(Ts... args) { printf("Do something!\n"); } template <typename R, typename... Args> struct entity; template <typename R, typename... Args> struct entity<R(Args...)> { using fn_invoke_t = R(*)(void*); struct empty_t{}; template <typename T> static R fn_invoke(Args&&... args) { if constexpr (std::is_same_v<T, empty_t>) { // empty_t专属逻辑 return myfunc(args...); } else { // 通用逻辑 return myfunc(args...); } } entity() : fn_invoke_(reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return fn_invoke<empty_t>(); } )) { } template <typename T> entity(T obj) : fn_invoke_( reinterpret_cast<fn_invoke_t>( +[](void*) -> R { return fn_invoke<T>(); } )) { } fn_invoke_t fn_invoke_; }; int main() { entity<void()> hello = [](){ printf("Hello World!\n"); }; }
内容的提问来源于stack exchange,提问作者glades
相关产品推荐
相关产品推荐

