You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

带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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 14:45:41