传递静态operator()作为删除器类型

dig*_*evo 4 c++ unique-ptr function-call-operator c++23

以下代码片段在 C++23 中合法吗?

#include <memory>
#include <cstdio>


int main()
{
    struct custom_deleter
    {
        static void operator()(int* const ptr)
        {
            delete ptr;
            std::fputs( "Deleted\n", stdout );
        }
    };

    auto ptr { std::unique_ptr<int, decltype(&custom_deleter::operator())> {new int {5},
                                                                            custom_deleter::operator()} };
}
Run Code Online (Sandbox Code Playgroud)

GCC 似乎对此很满意。不过我想知道它是否符合标准。

另外,为什么删除&from会decltype(&custom_deleter::operator())导致编译错误?这是否意味着Deleter类型模板参数必须是函数指针类型(在本例中void(*)(int*))?

Bar*_*rry 5

这是完全正确的。static operator()是一个新的 C++23 语言功能,并且您正在正确使用它。

然而,除非你真的特别想要一个函数指针删除器(因为......也许你想在某个时候交换不同的函数指针,并且你需要这种灵活性),否则你真的应该将整个类型保留在那里:

std::unique_ptr<int, custom_deleter>
Run Code Online (Sandbox Code Playgroud)

这将优化得更好

  • 如果你custom_deleter直接写,你只会看到对 的调用operator delete,而另一个版本只是call [QWORD PTR [rax]],因为它可以是任何东西
  • 也因为custom_deleter是一个空类型,sizeof(unique_ptr<int, custom_deleter>)所以只是8- sizeof(int*)。但是将函数指针隐藏在那里会将大小增加到 16。

另外,为什么删除&from会decltype(&custom_deleter::operator())导致编译错误?这是否意味着Deleter类型模板参数必须是函数指针类型?

因为decltype(&custom_deleter::operator())void(*)(int*)是一个指向函数的指针。但decltype(custom_deleter::operator())只是void(int*)。这是一个函数类型。

具体Deleter而言, 不一定是函数指针类型(如果有的话,您应该真正避免使用函数指针类型,除非您确实特别需要这种灵活性),但unique_ptr<T, Deleter>具有 type 的成员Deleter,因此Deleter最好是您可以的类型有一个非静态数据成员 - 适用于函数指针和函数对象,但不适用于普通函数。这就是为什么这&是必要的。


最后,我只想评论一下这种风格:

auto var { Type { ... } };
Run Code Online (Sandbox Code Playgroud)

请只写:

auto var = Type { ... };
Run Code Online (Sandbox Code Playgroud)

对于仅声明具有显式类型的变量的声明,无论如何都没有区别。这两个意思完全一样。我不知道为什么支持这种语法(无论如何都是无效的,如果你想要的话auto x{1, 2, 3};你必须写)。始终使用意味着所有变量声明看起来都相同,包括从函数调用等初始化的变量声明,并节省了额外的大括号嵌套,这使得其余部分更难以解析(对于人类来说,编译器对此没有问题)。auto x = {1, 2, 3};std::initializer_list<int>=