如何特化模板类X以从U*或shared_ptr<U>获取指针?
C++模板类
toPtr成员函数特化问题解决 问题背景
原模板类X定义如下:
template<typename T, uint32_t HandleTag = '_ptr'> struct X { void * toPtr(T t) { return 0; } std::string toHandle(T t) { const void *rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } };
需求是让T支持原始指针U*和智能指针(如std::shared_ptr<U>),尝试通过特化toPtr成员函数适配这两种类型,写出如下代码:
template<typename U> void * X<U*>::toPtr(U* t) { return t; } template<typename U> void * X<std::shared_ptr<U>>::toPtr(std::shared_ptr<U> t) { return t.get(); }
但编译器报错:
testptr.cpp:27:15: error: nested name specifier 'X<U *>::' for declaration does not refer into a class, class template or class template partial specialization void * X<U*>::toPtr(U* t) ~~~~~~~^ testptr.cpp:29:12: error: use of undeclared identifier 't' return t; ^ testptr.cpp:33:26: error: nested name specifier 'X<shared_ptr<U>>::' for declaration does not refer into a class, class template or class template partial specialization void * X<shared_ptr<U>>::toPtr(shared_ptr<U> t) ~~~~~~~~~~~~~~~~~~^ testptr.cpp:35:12: error: use of undeclared identifier 't' return t.get(); ^
错误原因
C++不允许直接对类模板的成员函数进行偏特化,除非先定义类模板的偏特化版本。你尝试的写法是直接针对X<U*>或X<std::shared_ptr<U>>的成员函数特化,但X<U*>只是类模板的一个实例化,并非预先声明的偏特化,因此编译器无法识别。
正确实现方式
方式一:类模板偏特化
针对原始指针和智能指针分别偏特化整个X类,重写toPtr成员函数,同时保留toHandle逻辑:
#include <string> #include <sstream> #include <memory> // 原通用模板 template<typename T, uint32_t HandleTag = '_ptr'> struct X { void * toPtr(T t) { return 0; } std::string toHandle(T t) { const void *rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } }; // 针对原始指针U*的偏特化 template<typename U, uint32_t HandleTag> struct X<U*, HandleTag> { void* toPtr(U* t) { return t; } std::string toHandle(U* t) { const void* rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } }; // 针对std::shared_ptr<U>的偏特化 template<typename U, uint32_t HandleTag> struct X<std::shared_ptr<U>, HandleTag> { void* toPtr(std::shared_ptr<U> t) { return t.get(); } std::string toHandle(std::shared_ptr<U> t) { const void* rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } };
方式二:SFINAE重载成员函数(避免重复代码)
在原类中通过std::enable_if实现toPtr的重载,自动匹配不同的T类型,无需偏特化整个类:
#include <string> #include <sstream> #include <memory> #include <type_traits> template<typename T, uint32_t HandleTag = '_ptr'> struct X { // 匹配原始指针类型U* template<typename U = T> std::enable_if_t<std::is_pointer_v<U>, void*> toPtr(U t) { return t; } // 匹配std::shared_ptr<U>类型 template<typename U = T> std::enable_if_t<std::is_same_v<U, std::shared_ptr<typename U::element_type>>, void*> toPtr(U t) { return t.get(); } // 通用 fallback 版本 void* toPtr(T t) { return 0; } std::string toHandle(T t) { const void *rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } };
方式三:C++20 概念(更简洁)
如果使用C++20,可以用std::pointer和std::shared_ptr概念约束重载,代码更直观:
#include <string> #include <sstream> #include <memory> #include <concepts> template<typename T, uint32_t HandleTag = '_ptr'> struct X { // 匹配原始指针 void* toPtr(std::pointer auto t) { return t; } // 匹配std::shared_ptr void* toPtr(std::shared_ptr auto t) { return t.get(); } // 通用 fallback 版本 void* toPtr(T t) { return 0; } std::string toHandle(T t) { const void *rawptr = toPtr(t); std::stringstream ss; for(int i = 24; i >= 0; i -= 8) ss << char((HandleTag >> i) & 0xFF); ss << ':' << rawptr; return ss.str(); } };
内容的提问来源于stack exchange,提问作者fferri
相关产品推荐
相关产品推荐

