如何将C++函数/Lambda作为非类型参数传入typedef/using模板?
FILE* f1 = fopen(xxx);
FILE* f2 = fopen(yyy);
smartFp sf1(f1); // 无需传递deleter函数
smartFp sf2(f2);
...
是否可以实现这种`sp_with_deleter`类型? # 解决方案 当然可以实现,但需要调整模板参数的写法——`std::shared_ptr`的第二个模板参数是**deleter的类型**,而非函数本身。下面提供几种实用的实现方式: ## 方式一:C++17+ 通用模板别名(auto非类型模板参数) 利用C++17支持的`auto`非类型模板参数,结合`std::integral_constant`将函数包装成符合要求的deleter类型: ```cpp #include <memory> #include <cstdio> #include <type_traits> template<typename T, auto Deleter> using sp_with_deleter = std::shared_ptr<T, std::integral_constant<decltype(Deleter), Deleter>>; // 客户端使用 using smartFp = sp_with_deleter<FILE*, fclose>; // 构造时无需手动传递deleter FILE* f1 = fopen("test.txt", "r"); smartFp sf1(f1);
std::integral_constant会把传入的函数指针包装成一个可调用对象,std::shared_ptr会自动调用它的operator()来释放资源。
方式二:封装模板类(兼容C++11及以上)
如果需要兼容更早的C++标准,或者想要添加自定义逻辑,可以封装一个模板类继承std::shared_ptr:
#include <memory> #include <cstdio> template<typename T, void (*Deleter)(T)> class SmartPtrWithDeleter : public std::shared_ptr<T> { public: explicit SmartPtrWithDeleter(T ptr) : std::shared_ptr<T>(ptr, Deleter) {} // 按需继承或添加其他构造函数、成员函数 }; // 客户端使用 using smartFp = SmartPtrWithDeleter<FILE*, fclose>; FILE* f1 = fopen("test.txt", "r"); smartFp sf1(f1); // 无需传deleter
这种方式可以灵活扩展,比如添加空指针检查、自定义日志等逻辑。
方式三:专属资源封装(最易维护)
如果某个资源的使用频率很高,直接封装专属的智能指针类型是最优选择:
#include <memory> #include <cstdio> struct FileDeleter { void operator()(FILE* fp) const { if (fp) { fclose(fp); // 后续可以在这里添加日志、错误处理等扩展逻辑 } } }; using SmartFilePtr = std::shared_ptr<FILE, FileDeleter>; // 客户端使用 FILE* f1 = fopen("test.txt", "r"); SmartFilePtr sf1(f1);
后续如果需要修改deleter逻辑,只需要修改FileDeleter的operator()即可,所有使用SmartFilePtr的代码都无需改动,完美契合你避免大量修改的需求。
总结
- 通用场景下,C++17+推荐用
auto模板参数结合std::integral_constant实现sp_with_deleter; - 需要兼容旧标准或扩展逻辑时,封装模板类是更好的选择;
- 针对特定资源,专属智能指针的封装方式最简洁、易维护。
内容的提问来源于stack exchange,提问作者gongliming7
相关产品推荐
相关产品推荐

