c++ std::unique_ptr自定义删除器似乎需要一个指针参数--有没有可以接受非指针参数的替代方法?

ffx8fchx  于 2023-02-20  发布在  其他
关注(0)|答案(1)|浏览(102)

我最近了解了使用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;
}

在上面的代码中,RAII_FD宏创建了一个std::unique_ptr对象,该对象的自定义删除器接受一个int*(指向文件描述符的指针),并在文件描述符上调用close()
我非常喜欢这种机制,但是我有一个小小的抱怨,即定制的deleter需要一个 pointer 作为它的参数:从美学上看,感觉有点不太理想。
例如,在上面的代码中,自定义删除器是int close(int)上的一个薄 Package 器--因此,如果自定义删除器可以接受int而不是int*,那就太好了......在这种情况下,可能根本不需要 Package 器函数:也许可以提供指向X1 M8 N1 X本身的函数指针。
例如,尝试了以下变体,尝试使用签名void func(int)而不是void func(int*)注册自定义删除器:

// 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;
}

...编译错误是一个stl错误呕吐,我可能没有100%,但我认为要点是有int*/int不匹配的各种模板扩展.

是否存在另一种类似的机制,通过该机制,可以使用自定义删除器实现“通用”RAII机制,而自定义删除器的参数不必是指针?

我愿意 * 学习 * 所有可能的解决方案,但在我的目标环境中实际可用的解决方案必须是C++11和非Boost。最可取的是在STL原生对象上使用类似的“薄 Package 器”。

dohp0rv5

dohp0rv51#

std::unique_ptr实际上并不只是对原生原始指针进行操作。它支持满足NullablePointer requirements的所有类型。基本上,该类型在值语义和相等比较方面的行为应该像标量类型一样,并且可以使用nullptr为空,进入一个不同的空状态,该状态也是值初始化状态。这要求您有一个状态来表示空状态,在您的情况下可以由-1提供。
这样的类型将作为自定义删除器的类型成员pointer提供给unique_ptr

// Code not tested!
// You must check that I didn't make a stupid mistake or forgot a requirement!

struct Deleter {
    struct pointer {
        int fd = -1;

        pointer() noexcept = default;
        pointer(std::nullptr_t) noexcept {}

        // should probably be explicit
        // but then `fd` cannot be passed directly to the
        // std::unique_ptr constructor
        pointer(int fd) noexcept : fd(fd) {};

        friend bool operator==(pointer p, pointer q) noexcept {
            return p.fd == q.fd;
        }

        // operator!= is optional with C++20
        friend bool operator!=(pointer p, pointer q) noexcept {
            return !(p == q);
        }

        operator bool() noexcept { return *this != nullptr; }
    };

    // unique_ptr calls this only if pointer doesn't represent the null state
    void operator()(pointer x) noexcept { close(x.fd); }
};

然后您可以使用

std::unique_ptr<int, Deleter> u(fd);

实际上元素类型是int并不重要。std::unique_ptr<double, Deleter>也可以工作。但是,你不能用*u来解引用。如果这是必需的,那么你需要给予pointer一个operator*,在这种情况下,元素类型应该匹配它返回的类型。
考虑到大量的样板代码和对pointer的不寻常的解释,这是否真的有用取决于您。

相关问题