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

huangapple go评论64阅读模式
英文:

Passing a static operator() as deleter type

问题

以下是您要翻译的代码部分:

#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()} };
}

关于您的问题,GCC似乎能够编译这段代码,但我不确定它是否符合C++23标准。

另外,从decltype(&custom_deleter::operator())中删除&会导致编译错误,这是因为decltype需要获取成员函数指针的地址。这意味着Deleter类型模板参数必须是函数指针类型(在这种情况下是void(*)(int*))。

英文:

Is the following code snippet legal in C++23?

#include &lt;memory&gt;
#include &lt;cstdio&gt;


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

    auto ptr { std::unique_ptr&lt;int, decltype(&amp;custom_deleter::operator())&gt; {new int {5},
                                                                            custom_deleter::operator()} };
}

GCC seems to be fine with it. However, I wonder whether or not it is standard-compliant.

Also, why does removing the &amp; from decltype(&amp;custom_deleter::operator()) cause a compile error? Does this mean that the Deleter type template parameter has to be a function pointer type (in this case void(*)(int*))?

答案1

得分: 5

这是完全有效的。static operator() 是一个新的 C++23 语言特性,你使用得很正确。

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

std::unique_ptr<int, custom_deleter>

这将优化得更好

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

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

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

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

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

auto var { Type { ... } };

请只写:

auto var = Type { ... };

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

英文:

This is perfectly valid. static operator() is a new C++23 language feature, and you're using it correctly.

However, unless you really specifically want a function pointer deleter (because... maybe you want to swap a different function pointer at some point, and you need that flexibility), you really should keep the whole type in there:

std::unique_ptr&lt;int, custom_deleter&gt;

This will optimize much better:

  • if you write custom_deleter directly, you just see a call to operator delete, while the other version is just call [QWORD PTR [rax]], cause it could be anything
  • also because custom_deleter is an empty type, sizeof(unique_ptr&lt;int, custom_deleter&gt;) is just 8 - sizeof(int*). But stashing the function pointer in there ups the size to 16.

> Also, why does removing the &amp; from decltype(&amp;custom_deleter::operator()) cause a compile error? Does this mean that the Deleter type template parameter has to be a function pointer type?

Because decltype(&amp;custom_deleter::operator()) is void(*)(int*) - it's a pointer to function. But decltype(custom_deleter::operator()) is just void(int*). That's a function type.

The Deleter doesn't have to be a function pointer type, specifically (if anything, you should really avoid using function pointer types unless you really specifically need that flexibility), but unique_ptr&lt;T, Deleter&gt; has a member of type Deleter, so Deleter had better be a type that you can have a non-static data member of - that works for function pointers and function objects, but not plain functions. That's why the &amp; is necessary.


Lastly, I just want to comment on this style:

auto var { Type { ... } };

Please just write:

auto var = Type { ... };

For declarations where you're just declaring a variable with explicit type like this, there's no difference anyway. Those two mean exactly the same thing. I have no idea why this syntax is even supported (auto x{1, 2, 3}; is invalid anyway, you have to write auto x = {1, 2, 3}; if you want a std::initializer_list&lt;int&gt;). Always using = means all your variable declarations look the same, including the ones initialized from function calls, etc, and saves you the extra nesting of braces which makes the rest harder to parse (for humans, the compiler is fine with it).

huangapple
  • 本文由 发表于 2023年6月2日 08:56:42
  • 转载请务必保留本文链接:https://go.coder-hub.com/76386548.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定