11

理论上,我应该能够使用自定义指针类型和删除器来unique_ptr管理不是指针的对象。我尝试了以下代码:

#ifndef UNIQUE_FD_H
#define UNIQUE_FD_H

#include <memory>
#include <unistd.h>

struct unique_fd_deleter {
    typedef int pointer; // Internal type is a pointer

    void operator()( int fd )
    {
        close(fd);
    }
};

typedef std::unique_ptr<int, unique_fd_deleter> unique_fd;

#endif // UNIQUE_FD_H

这不起作用(带有-std=c++11参数的 gcc 4.7)。它响应以下错误:

In file included from /usr/include/c++/4.7/memory:86:0,
                 from test.cc:6:
/usr/include/c++/4.7/bits/unique_ptr.h: In instantiation of 'std::unique_ptr<_Tp, _Dp>::~unique_ptr() [with _Tp = int; _Dp = unique_fd_deleter]':
test.cc:22:55:   required from here
/usr/include/c++/4.7/bits/unique_ptr.h:172:2: error: invalid operands of types 'int' and 'std::nullptr_t' to binary 'operator!='

通过深入研究 的定义unique_ptr,我可以看到阻止它工作的两个问题。第一个似乎明显违反标准,析构函数unique_ptr将“指针”(根据我的定义,即一个 int)与它进行比较nullptr,以查看它是否已初始化。这与它通过布尔转换报告它的方式形成对比,布尔转换是将它与"pointer()"(未初始化的“指针”)进行比较。这是我看到的错误的原因 - 整数与nullptr.

第二个问题是我需要一些方法来判断unique_ptr未初始化的值是什么。我希望以下代码段起作用:

unique_fd fd( open(something...) );

if( !fd )
    throw errno_exception("Open failed");

为此,unique_ptr需要知道“未初始化的值”是-1,因为零是有效的文件描述符。

这是一个错误gcc,还是我想在这里做一些根本无法完成的事情?

4

9 回答 9

12

所暴露的类型Deleter::pointer必须满足NullablePointer要求。其中最主要的是,这个表达必须是合法的:Deleter::pointer p = nullptr;. 当然,nullptr它的定义很大程度上是因为它不能隐式转换为数字,因此这是行不通的。

您必须使用可以隐式构造的类型std::nullptr_t。像这样的东西:

struct file_desc
{
  file_desc(int fd) : _desc(fd) {}
  file_desc(std::nullptr_t) : _desc(-1) {}

  operator int() {return _desc;}

  bool operator ==(const file_desc &other) const {return _desc == other._desc;}
  bool operator !=(const file_desc &other) const {return _desc != other._desc;}
  bool operator ==(std::nullptr_t) const {return _desc == -1;}
  bool operator !=(std::nullptr_t) const {return _desc != -1;}

  int _desc;
};

您可以将其用作Deleter::pointer类型。

于 2013-04-02T06:32:52.093 回答
10

你能做一些像下面这样简单的事情吗?

class unique_fd {
public:
    unique_fd(int fd) : fd_(fd) {}
    unique_fd(unique_fd&& uf) { fd_ = uf.fd_; uf.fd_ = -1; }
    ~unique_fd() { if (fd_ != -1) close(fd_); }

    explicit operator bool() const { return fd_ != -1; }

private:
    int fd_;

    unique_fd(const unique_fd&) = delete;
    unique_fd& operator=(const unique_fd&) = delete;
};

我不明白您为什么必须使用unique_ptr旨在管理指针的 。

于 2013-04-02T11:14:09.553 回答
2

一个完整的样本:

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

template<class T, T nullvalue, class D, D d> // auto d works in new compilers.
struct generic_delete
{
    class pointer
    {
        T t;
    public:
        pointer(T t) : t(t) {}
        pointer(std::nullptr_t = nullptr) : t(nullvalue) { }
        explicit operator bool() { return t != nullvalue; }
        friend bool operator ==(pointer lhs, pointer rhs) { return lhs.t == rhs.t; }
        friend bool operator !=(pointer lhs, pointer rhs) { return lhs.t != rhs.t; }
        operator T() { return t; }
    };
    void operator()(pointer p)
    {
        d(p);
    }
};
using unique_fd = std::unique_ptr<void, generic_delete<int, -1, decltype(&close), &close>>;
static_assert(sizeof(unique_fd) == sizeof(int), "bloated unique_fd");

int main()
{
    unique_fd fd1(open("fd.txt", O_WRONLY | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR));
    write(fd1.get(), "hello\n", 6);
}
于 2018-02-02T03:44:06.670 回答
1

开源 Android 框架定义了一个可以满足您需求的 unique_fd 类:https ://android.googlesource.com/platform/system/core/+/c0e6e40/base/include/android-base/unique_fd.h

于 2018-12-06T21:22:30.323 回答
1

在cppreference.com找到答案。查看示例代码:

    void close_file(std::FILE* fp) { std::fclose(fp); }
    ...
    {
      std::unique_ptr<std::FILE, decltype(&close_file)> fp(std::fopen("demo.txt",                                                             
                                                                      "r"), 
                                                           &close_file);
      if(fp)     // fopen could have failed; in which case fp holds a null pointer
         std::cout << (char)std::fgetc(fp.get()) << '\n';
    }// fclose() called here, but only if FILE* is not a null pointer
     // (that is, if fopen succeeded)

在vs2019中试了一下,效果很好!还使用 member 和 lambda 进行了尝试:

文件测试.h:

class A
{
  std::unique_ptr<std::FILE, std::function<void(std::FILE*)>> fp;
}

文件测试.cpp

void A::OpenFile(const char* fname)
{
    fp = std::unique_ptr < std::FILE, std::function<void(std::FILE*)>>(
        std::fopen(fname, "wb"),
        [](std::FILE * fp) { std::fclose(fp); });
}
于 2020-04-22T08:47:42.930 回答
0

该解决方案基于 Nicol Bolas 的回答:

struct FdDeleter
{
    typedef int pointer;
    void operator()(int fd)
    {
        ::close(fd);
    }
};
typedef std::unique_ptr<int, FdDeleter> UniqueFd;

它很短,但您必须避免将 UniqueFd 实例与 nullptr 进行比较并将其用作布尔表达式:

UniqueFd fd(-1, FdDeleter()); //correct
//UniqueFd fd(nullptr, FdDeleter()); //compiler error
if (fd.get() != -1) //correct
{
    std::cout << "Ok: it is not printed" << std::endl;
}
if (fd) //incorrect, avoid
{
    std::cout << "Problem: it is printed" << std::endl;
}
if (fd != nullptr) //incorrect, avoid
{
    std::cout << "Problem: it is printed" << std::endl;
}
return 1;
于 2015-06-26T08:45:26.720 回答
0

使 Nicol Bolas 类更通用:

template<class T=void*,T null_val=nullptr>
class Handle
    {
    public:
        Handle(T handle):m_handle(handle){}

        Handle(std::nullptr_t):m_handle(null_val){}

        operator T(){return m_handle;}

        bool operator==(const Handle& other) const
            {return other.m_handle==m_handle;}

    private:
        T m_handle;
    };

typedef Handle<int,-1> FileDescriptor;
typedef Handle<GLuint,0> GlResource; // according to http://stackoverflow.com/questions/7322147/what-is-the-range-of-opengl-texture-id
// ...

我不确定我是否应该有默认的模板参数值。

于 2016-06-19T08:40:30.397 回答
0

我建议使用shared_ptr而不是unique_ptr管理int句柄的生命周期,因为共享所有权语义通常更合适,并且删除器的类型已被删除。您需要以下助手:

namespace handle_detail
{
    template <class H, class D>
    struct deleter
    {
        deleter( H h, D d ): h_(h), d_(d) { }
        void operator()( H * h ) { (void) d_(h_); }
        H h_;
        D d_;
    };
}

template <class H,class D>
std::shared_ptr<H const>
make_handle( H h, D d )
{
    std::shared_ptr<H> p((H *)0,handle_detail::deleter<H,D>(h,d));
    return std::shared_ptr<H const>(
        p,
        &std::get_deleter<handle_detail::deleter<H,D> >(p)->h_ );
}

要与文件描述符一起使用:

int fh = open("readme.txt", O_RDONLY); // Check for errors though.
std::shared_ptr<int const> f = make_handle(fh, &close);
于 2019-08-08T06:03:57.163 回答
0

不要将(智能)指针强制为非指针对象。

理论上,我应该能够使用自定义指针类型和删除器来unique_ptr管理不是指针的对象。

不,你不应该。也就是说,就让它编译和运行而言,也许,但你根本不应该使用 aunique_ptr来管理不是指针的东西。您绝对应该为您的资源编写适当的 RAII 类 - 例如 OS 文件描述符 - 或使用某个库中现有的此类类。只有当您想要指向此类资源的指针时,unique_ptr 才有意义;但是,您不需要自定义删除器。

于 2020-04-22T08:52:06.023 回答