带auto返回类型的函数指针能否作为C++模板参数?
关于
std::unique_ptr自定义删除器模板参数合法性的问题 我在使用std::unique_ptr时,习惯定义如下删除器类:
template<typename T, void (*Free)(T*)> struct MyDeleter { void operator()(T* ctx) const { Free(ctx); } };
通过该类可便捷定义不同的unique_ptr类型,示例如下:
using my_x_ptr = std::unique_ptr<my_x, MyDeleter<my_x, &my_x_free>>;
但问题在于,有时删除器Free方法的返回值并非void,此前我会通过SFINAE编写泛型类来解决。后来我尝试将模板参数改为auto (*Free)(T*):
template<typename T, auto (*Free)(T*)> struct MyDeleter { void operator()(T* ctx) const { Free(ctx); } };
发现该类可匹配任意返回类型的Free方法,大幅简化代码。经测试,该写法在Clang中可行,但GCC不支持,我不确定该模板参数写法是否合法,还是属于无需诊断的未定义行为范畴?
补充代码示例:
template<typename T, auto (*Free)(T*)> struct MyDeleter { void operator()(T* ctx) const { Free(ctx); } }; void s_free1(S* x) { delete x; } int s_free2(S* x) { delete x; return 0; } using s_ptr1 = std::unique_ptr<S, MyDeleter<S, &s_free1>>; using s_ptr2 = std::unique_ptr<S, MyDeleter<S, &s_free2>>; int main() { s_ptr1 p1(new S{}); s_ptr2 p2(new S{}); }
结论
这种模板参数写法是合法的C++17及以后标准语法,并非未定义行为。GCC早期版本对该特性的支持存在滞后,升级到GCC 9及以上版本即可正常编译。
原理说明
- C++17引入了
auto作为非类型模板参数的占位符,允许编译器推导非类型模板参数的具体类型。这里的auto (*Free)(T*)表示一个指向“接受T*类型参数、返回任意类型”的函数的指针模板参数,编译器会根据传入的函数指针自动推导返回值类型,从而适配不同返回类型的删除函数。 std::unique_ptr对删除器的要求仅为:可调用、接受T*类型参数,删除器的调用返回值会被忽略(因为unique_ptr的析构逻辑不需要使用该返回值),所以即使删除函数返回非void类型,也完全符合要求。
兼容旧编译器的替代方案
如果需要兼容不支持C++17或对该特性支持不完善的编译器,可以选用以下两种方案:
方案1:模板函数推导删除器类型
template<typename T, typename F> struct MyDeleter { F free_func; void operator()(T* ctx) const { free_func(ctx); } }; // 辅助推导函数 template<typename T, typename F> auto make_my_deleter(F&& f) { return MyDeleter<T, std::decay_t<F>>{std::forward<F>(f)}; } // 使用示例 using s_ptr1 = std::unique_ptr<S, decltype(make_my_deleter<S>(&s_free1))>; using s_ptr2 = std::unique_ptr<S, decltype(make_my_deleter<S>(&s_free2))>;
方案2:泛型lambda作为删除器
// 直接用lambda封装删除函数 auto s_deleter1 = [](S* x) { s_free1(x); }; auto s_deleter2 = [](S* x) { s_free2(x); }; // 定义unique_ptr类型 using s_ptr1 = std::unique_ptr<S, decltype(s_deleter1)>; using s_ptr2 = std::unique_ptr<S, decltype(s_deleter2)>;
内容的提问来源于stack exchange,提问作者Afshin
相关产品推荐
相关产品推荐

