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

std::unique_ptr自定义删除器能否用非指针参数?求C++11替代方案

问题描述

我最近尝试用std::unique_ptr实现通用RAII机制来管理文件描述符,示例代码如下:

// main.cpp

#include <memory>
#include <sys/fcntl.h>
#include <unistd.h>

#define RAII_CAT(x) raii ## x

#define RAII_CAT2(x) RAII_CAT(x)

#define RAII_FD(X) \
    std::unique_ptr<int, void(*)(int*)> RAII_CAT2(__LINE__){X, [](int* x){ if (-1 != *x) { close(*x); }}}

int main(int argc, char* argv[]) {
  {
    int fd = open("foo.txt", O_RDONLY);
    RAII_FD(&fd);
  }

end:
  return 0;
}

这里的问题是,std::unique_ptr的自定义删除器必须接收int*类型参数,但close()本身只需要int类型参数。我尝试把删除器改成接收int类型时,出现了int*/int类型不匹配的编译错误。

现在需要在C++11、非Boost环境下,找到一种通用RAII的替代方案,要求自定义删除器不需要接收指针参数,优先用原生STL的轻量实现。


方案1:自定义轻量通用RAII模板类

直接实现一个通用的RAII包装类,将资源和销毁函数绑定,完全摆脱unique_ptr的指针语义限制,删除器直接接收资源本身:

#include <functional>
#include <sys/fcntl.h>
#include <unistd.h>

template<typename T>
class RAIIWrapper {
public:
    using Deleter = std::function<void(T)>;

    RAIIWrapper(T resource, Deleter deleter)
        : m_resource(resource), m_deleter(std::move(deleter)), m_valid(true) {}

    // 禁止拷贝
    RAIIWrapper(const RAIIWrapper&) = delete;
    RAIIWrapper& operator=(const RAIIWrapper&) = delete;

    // 支持移动语义
    RAIIWrapper(RAIIWrapper&& other) noexcept
        : m_resource(other.m_resource), m_deleter(std::move(other.m_deleter)), m_valid(other.m_valid) {
        other.m_valid = false;
    }

    RAIIWrapper& operator=(RAIIWrapper&& other) noexcept {
        if (this != &other) {
            if (m_valid) {
                m_deleter(m_resource);
            }
            m_resource = other.m_resource;
            m_deleter = std::move(other.m_deleter);
            m_valid = other.m_valid;
            other.m_valid = false;
        }
        return *this;
    }

    ~RAIIWrapper() {
        if (m_valid) {
            m_deleter(m_resource);
        }
    }

    // 获取原始资源
    T get() const { return m_resource; }

private:
    T m_resource;
    Deleter m_deleter;
    bool m_valid; // 标记资源有效性,避免重复销毁
};

// 文件描述符便捷宏
#define RAII_FD(FD) \
    RAIIWrapper<int> RAII_CAT2(__LINE__)(FD, [](int fd) { if (fd != -1) close(fd); })

// 宏拼接逻辑
#define RAII_CAT(x) raii ## x
#define RAII_CAT2(x) RAII_CAT(x)

int main(int argc, char* argv[]) {
    {
        int fd = open("foo.txt", O_RDONLY);
        RAII_FD(fd); // 直接传入int类型的fd,无需指针
        // 正常使用fd进行操作
    }
    return 0;
}

这个方案的优势:

  • 删除器直接接收资源本身(如int类型的fd),完全匹配close()的参数要求
  • 模板类通用,可复用在socket、自定义句柄等其他资源管理场景
  • 纯C++11 STL实现,无外部依赖,开销极小

方案2:基于std::unique_ptr的资源包装方案

如果一定要依赖std::unique_ptr,可以将资源包装为单元素结构体,让unique_ptr管理结构体指针,删除器通过结构体访问原始资源:

#include <memory>
#include <sys/fcntl.h>
#include <unistd.h>

// 包装文件描述符的结构体
struct FDHolder {
    int fd;
    explicit FDHolder(int f) : fd(f) {}
};

#define RAII_FD(FD) \
    std::unique_ptr<FDHolder, void(*)(FDHolder*)> RAII_CAT2(__LINE__)( \
        new FDHolder(FD), [](FDHolder* holder) { \
            if (holder->fd != -1) close(holder->fd); \
            delete holder; \
        })

#define RAII_CAT(x) raii ## x
#define RAII_CAT2(x) RAII_CAT(x)

int main(int argc, char* argv[]) {
    {
        int fd = open("foo.txt", O_RDONLY);
        auto raii_fd = RAII_FD(fd);
        // 通过raii_fd->fd访问原始文件描述符
    }
    return 0;
}

该方案基于unique_ptr实现,但需要额外分配结构体内存,相比方案1稍重,适合必须使用unique_ptr的场景。


内容的提问来源于stack exchange,提问作者StoneThrow

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.31 09:30:58