英文:
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 <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 seems to be fine with it. However, I wonder whether or not it is standard-compliant.
Also, why does removing the &
from decltype(&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<int, custom_deleter>
This will optimize much better:
- if you write
custom_deleter
directly, you just see a call tooperator delete
, while the other version is justcall [QWORD PTR [rax]]
, cause it could be anything - also because
custom_deleter
is an empty type,sizeof(unique_ptr<int, custom_deleter>)
is just8
-sizeof(int*)
. But stashing the function pointer in there ups the size to 16.
> Also, why does removing the &
from decltype(&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(&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<T, Deleter>
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 &
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<int>
). 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).
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论