如何制作一个 class 模板,将函数包装在一个 noexcept-detectable 可调用对象中,用作 std::unique_ptr 自定义删除器?
How to make a class template that wraps a function in a noexcept-detectable, callable object, to use as a std::unique_ptr custom deleter?
是否可以使用对象类型和自由函数作为参数来为 std::unique_ptr
创建自定义删除器?
我是模板新手,来到这里:
#include <memory>
template<typename T, typename FreeFunc> struct ObjectDeleter {
const FreeFunc &free_func;
ObjectDeleter(const FreeFunc &free_func) : free_func(free_func){
}
void operator()(T *item)
{
if (item) {
free_func(item);
}
}
};
struct Foo{};
void internal_foo_free(Foo *){}
struct Bar{};
void internal_bar_free(Bar *){}
using unique_foo_ptr =
std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
int main(){
return 0;
}
错误:
<source>:19:48: error: wrong number of template arguments (1, should be 2)
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
<source>:3:48: note: provided for 'template<class T, class FreeFunc> struct ObjectDeleter'
3 | template<typename T, typename FreeFunc> struct ObjectDeleter {
| ^~~~~~~~~~~~~
<source>:19:50: error: lambda-expression in template-argument only available with '-std=c++2a' or '-std=gnu++2a'
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
<source>:19:87: error: template argument 2 is invalid
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
Compiler returned: 1
有人建议我使用函数指针(我也将其扩展为 std::function):
但这增加了通过函数指针(或std::function)添加抛出语句的可能性,编译器或静态分析器将无法检测到。使用 lambda 进行参数化将确保没有人可以在 std::unique_ptr 的析构函数中添加抛出语句。这就是我所说的“noexcept-detectable, callable object”的意思
我正在使用 C++17。
这是抱怨,因为 lambda 不能用作模板参数(无论如何在 C++20 之前)。否则,lambdas 已经 可调用 objects 除非函数 body 抛出,否则不会抛出,不需要包装器 class。你只需要做这个尴尬:
auto myDeleter = [&x](Foo* v) { internal_foo_free(v); };
std::unique_ptr<Foo, decltype(myDeleter)> guard { create_foo(), myDeleter };
最初,我将这个问题解释为“如果有人使用未标记 noexcept
的自定义删除器,我希望编译失败”。我问过这个问题,我认为这就是为什么您编辑标题以包含该措辞的原因。
noexcept
是用于优化 hint/documentation 目的的限定符。这完全取决于荣誉制度。您可以直接将其放入其中,您的源代码仍会编译(尽管静态分析器可能会抱怨)。如果你想强制自定义删除器只调用 noexcept
函数,在 C++17 中你可以使用带有 noexcept
运算符的静态断言,如果表达式调用一个 returns false non-noexcept 函数:
template <auto F>
struct NoExceptDeleter{
template <typename T>
void operator ()(T* arg) const noexcept {
static_assert(noexcept(F(arg)), "deleter must be marked noexcept");
F(arg);
}
};
void good_delete(foo* v) noexcept { free(v); }
std::unique_ptr<foo, NoExceptDeleter<good_delete>> good_guard { make_foo() };
void bad_delete(foo* v) { throw 0; }
std::unique_ptr<foo, NoExceptDeleter<bad_delete>> bad_guard { make_foo() }; // compile-error
因为这需要一个函数指针,在 C++17 中你只能将它与 non-capturing lambdas 一起使用 +
运算符衰减为函数指针:
auto good_lambda = [](foo* v) noexcept { free(v); }
std::unique_ptr<foo, NoExceptDeleter<+good_lambda>> lambda_guard;
演示:https://godbolt.org/z/vdEov3
如果您需要在 lambda 内部进行捕获,则必须使用有状态删除器:
template <typename F>
struct StatefulNoExceptDeleter {
F f;
StatefulNoExceptDeleter(F f) : f(f) { }
template <typename T>
void operator ()(T* arg) const noexcept {
static_assert(noexcept(f(arg)), "deleter must be marked noexcept");
f(arg);
}
};
/* ... */
int baz;
auto deleter = [&](Foo* t) noexcept {
std::cout << "labmda: " << baz << std::endl;
delete t;
};
std::unique_ptr<Foo, StatefulNoExceptDeleter<decltype(deleter)>> guard {
new Foo,
StatefulNoExceptDeleter<decltype(deleter)>{ deleter }
};
是否可以使用对象类型和自由函数作为参数来为 std::unique_ptr
创建自定义删除器?
我是模板新手,来到这里:
#include <memory>
template<typename T, typename FreeFunc> struct ObjectDeleter {
const FreeFunc &free_func;
ObjectDeleter(const FreeFunc &free_func) : free_func(free_func){
}
void operator()(T *item)
{
if (item) {
free_func(item);
}
}
};
struct Foo{};
void internal_foo_free(Foo *){}
struct Bar{};
void internal_bar_free(Bar *){}
using unique_foo_ptr =
std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
int main(){
return 0;
}
错误:
<source>:19:48: error: wrong number of template arguments (1, should be 2)
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
<source>:3:48: note: provided for 'template<class T, class FreeFunc> struct ObjectDeleter'
3 | template<typename T, typename FreeFunc> struct ObjectDeleter {
| ^~~~~~~~~~~~~
<source>:19:50: error: lambda-expression in template-argument only available with '-std=c++2a' or '-std=gnu++2a'
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
<source>:19:87: error: template argument 2 is invalid
19 | std::unique_ptr<Foo, ObjectDeleter<Foo>([](Foo *foo){internal_foo_free(foo);});
| ^
Compiler returned: 1
有人建议我使用函数指针(我也将其扩展为 std::function):
但这增加了通过函数指针(或std::function)添加抛出语句的可能性,编译器或静态分析器将无法检测到。使用 lambda 进行参数化将确保没有人可以在 std::unique_ptr 的析构函数中添加抛出语句。这就是我所说的“noexcept-detectable, callable object”的意思
我正在使用 C++17。
这是抱怨,因为 lambda 不能用作模板参数(无论如何在 C++20 之前)。否则,lambdas 已经 可调用 objects 除非函数 body 抛出,否则不会抛出,不需要包装器 class。你只需要做这个尴尬:
auto myDeleter = [&x](Foo* v) { internal_foo_free(v); };
std::unique_ptr<Foo, decltype(myDeleter)> guard { create_foo(), myDeleter };
最初,我将这个问题解释为“如果有人使用未标记 noexcept
的自定义删除器,我希望编译失败”。我问过这个问题,我认为这就是为什么您编辑标题以包含该措辞的原因。
noexcept
是用于优化 hint/documentation 目的的限定符。这完全取决于荣誉制度。您可以直接将其放入其中,您的源代码仍会编译(尽管静态分析器可能会抱怨)。如果你想强制自定义删除器只调用 noexcept
函数,在 C++17 中你可以使用带有 noexcept
运算符的静态断言,如果表达式调用一个 returns false non-noexcept 函数:
template <auto F>
struct NoExceptDeleter{
template <typename T>
void operator ()(T* arg) const noexcept {
static_assert(noexcept(F(arg)), "deleter must be marked noexcept");
F(arg);
}
};
void good_delete(foo* v) noexcept { free(v); }
std::unique_ptr<foo, NoExceptDeleter<good_delete>> good_guard { make_foo() };
void bad_delete(foo* v) { throw 0; }
std::unique_ptr<foo, NoExceptDeleter<bad_delete>> bad_guard { make_foo() }; // compile-error
因为这需要一个函数指针,在 C++17 中你只能将它与 non-capturing lambdas 一起使用 +
运算符衰减为函数指针:
auto good_lambda = [](foo* v) noexcept { free(v); }
std::unique_ptr<foo, NoExceptDeleter<+good_lambda>> lambda_guard;
演示:https://godbolt.org/z/vdEov3
如果您需要在 lambda 内部进行捕获,则必须使用有状态删除器:
template <typename F>
struct StatefulNoExceptDeleter {
F f;
StatefulNoExceptDeleter(F f) : f(f) { }
template <typename T>
void operator ()(T* arg) const noexcept {
static_assert(noexcept(f(arg)), "deleter must be marked noexcept");
f(arg);
}
};
/* ... */
int baz;
auto deleter = [&](Foo* t) noexcept {
std::cout << "labmda: " << baz << std::endl;
delete t;
};
std::unique_ptr<Foo, StatefulNoExceptDeleter<decltype(deleter)>> guard {
new Foo,
StatefulNoExceptDeleter<decltype(deleter)>{ deleter }
};