使用普通函数删除器构建std :: unique_ptr的包装器

lve*_*lla 5 c++ templates c++11 c++14

我试图实现一个std::unique_ptr我可以这样使用的工厂:

auto fd = my_make_unique<fclose>(fopen("filename", "r"));
Run Code Online (Sandbox Code Playgroud)

即,将删除函数作为模板参数传递.

我在C++ 11中的最佳尝试是:

template<typename D, D deleter, typename P>
struct Deleter {
    void operator()(P* ptr) {
        deleter(ptr);
    }
};

template<typename D, D deleter, typename P>
std::unique_ptr<P, Deleter<D, deleter, P>> my_make_unique(P* ptr)
{
    return std::unique_ptr<P, Deleter<D, deleter, P>>(ptr);
}
Run Code Online (Sandbox Code Playgroud)

在C++ 14中它更清晰:

template<typename D, D deleter, typename P>
auto my_make_unique(P* ptr)
{
    struct Deleter {
        void operator()(P* ptr) {
            deleter(ptr);
        }
    };
    return std::unique_ptr<P, Deleter>(ptr);
}
Run Code Online (Sandbox Code Playgroud)

但是这两种解决方案都要求我将自己&fclose之前的类型fclose作为模板参数传递:

auto fd = my_make_unique<decltype(&fclose), fclose>(fopen("filename", "r"));
Run Code Online (Sandbox Code Playgroud)

是否有可能摆脱decltype(&fclose)C++ 11中的模板参数?在C++ 14中怎么样?

编辑:为什么这个问题不是CII中RAII和智能指针的重复:引用的问题是关于C++中的一般RAII技术,以及std::unique_ptr可用于此目的的答案之一.我已经熟悉RAII模式以及如何std::unique_ptr解决方案,但我关注如何构建一个更容易使用的抽象来解决我在与C库交互时遇到的这种常见情况.

Bar*_*rry 5

是否可以摆脱decltype(&fclose)C++11 中的模板参数?在 C++14 中呢?

不,直到 C++17 才能摆脱该参数的类型。模板非类型参数需要一个类型,您无法推断出该类型 - 因为它必须是模板非类型参数。这是一个问题。

此外,您还会遇到未指定标准库中函数地址的问题。例如,标准库总是允许提供额外的重载,因此&fclose可能是无效的。唯一真正可移植的方法是提供 lambda 或编写自己的包装函数:

auto my_fclose_lam = [](std::FILE* f) { std::fclose(f); }
void my_fclose_fun(std::FILE* f) { std::fclose(f); }
Run Code Online (Sandbox Code Playgroud)

使用其中任何一个,最多使用 C++14,您可以引入一个宏,例如:

#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(my_fclose_lam)>(fopen("filename", "r"));
Run Code Online (Sandbox Code Playgroud)

C++17 允许您至少通过以下方式将自定义函数提升为模板参数(尽管还不是 lambda)template auto

template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
    struct Deleter {
        void operator()(P* ptr) {
            deleter(ptr);
        }
    };
    return std::unique_ptr<P, Deleter>(ptr);
}

my_make_unique<my_fclose_fun>(fopen(...));
Run Code Online (Sandbox Code Playgroud)

C++20 最终将允许您将 lambda 插入其中:

my_make_unique<[](std::FILE* f){ std::fclose(f); }>(fopen(...));
Run Code Online (Sandbox Code Playgroud)

旧的错误答案:

所以你能做的最好的事情就是引入一个宏,比如:

#define DECL(v) decltype(v), v
auto fd = my_make_unique<DECL(&fclose)>(fopen("filename", "r"));
Run Code Online (Sandbox Code Playgroud)

您是否认为这是一个好主意可能取决于您的同事。


在 C++17 中,使用template auto,您可以编写my_make_unique<fclose>,这很棒:

template <auto deleter, typename P>
auto my_make_unique(P* ptr)
{
    struct Deleter {
        void operator()(P* ptr) {
            deleter(ptr);
        }
    };
    return std::unique_ptr<P, Deleter>(ptr);
}
Run Code Online (Sandbox Code Playgroud)