【发布时间】:2023-03-11 20:37:01
【问题描述】:
我目前正在使用一个定义了许多数据类型的 C 库,所有这些数据类型都需要由用户管理它们的生命周期。有许多以这种方式定义的函数:
int* create() {
return new int();
}
void destroy(int* i) {
delete i;
}
其中大部分不需要在创建后访问。他们只需要存在。正因为如此,我正在尝试使用 unique_ptr 在我需要它们生存的范围内声明的来管理它们。
这样的声明是这样的:
// Note that I'm avoiding writing the type's name manually.
auto a = std::unique_ptr<std::decay_t<decltype(*create())>, decltype(&destroy)>{create(), &destroy};
但是这太冗长了,所以我把它封装在一个实用函数模板中:
template<typename T>
auto make_unique_ptr(T* p, void (*f)(T*)) {
return std::unique_ptr<T, decltype(f)>(p, f);
}
这样用的:
auto b = make_unique_ptr(create(), &destroy);
这看起来不错,但引入了一个非标准函数,除了作为某些声明的语法糖之外没有任何实际用途。我的同事甚至可能不知道它的存在,并最终创建了具有不同名称的其他版本。
c++17 介绍了class template argument deduction。我认为这是解决我的问题的完美解决方案:一种推断所有这些类型的标准方法,而无需求助于用户定义的包装器。所以我尝试了这个:
auto c = std::unique_ptr{create(), &destroy};
按照 C++ 编译器和模板的规则,这会失败并显示几行长的错误消息。以下是相关部分:
(...): error: class template argument deduction failed:
auto c = std::unique_ptr{create(), &destroy};
^
(...): note: candidate: 'template<class _Tp, class _Dp>
unique_ptr(std::unique_ptr<_Tp, _Dp>::pointer, typename std::remove_reference<_Dp>::type&&)-> std::unique_ptr<_Tp, _Dp>'
unique_ptr(pointer __p,
^~~~~~~~~~
(...): note: template argument deduction/substitution failed:
(...): note: couldn't deduce template parameter '_Tp'
auto c = std::unique_ptr{create(), &destroy};
^
理论上,我可以添加一个演绎指南来处理这个问题:
namespace std {
template<typename T>
unique_ptr(T* p, void (*f)(T*)) -> unique_ptr<T, decltype(f)>;
}
至少在我的 gcc 版本上它确实有效,但标准不太喜欢它:
[命名空间.std]
1 除非另有说明,否则如果 C++ 程序向命名空间 std 或命名空间 std 内的命名空间添加声明或定义,则其行为未定义。
还有一些与区分指针和数组有关的问题,但让我们忽略它。
最后,问题:在使用自定义删除器时,是否有任何其他方法可以“帮助”std::unique_ptr(或者可能是std::make_unique)来推断正确的类型?以防万一这是一个 XY 问题,对于这些类型的生命周期管理,是否有任何我没有想到的解决方案(可能是 std::shared_ptr)?如果这两个答案都是否定的,那么c++20 是否有任何改进我应该期待,以解决这个问题?
【问题讨论】:
-
FWIW,除非您需要在运行时更改删除器,否则不要使用这样的函数指针删除器。没有它,使用显式类型
sizeof(unique_ptr<T>) == sizeof(T*)。有了它,sizeof(unique_ptr<T>) == sizeof(T*) + sizeof(deleter),在这种情况下是两倍大小 -
为什么不使用
make_library_type_nnn()来返回正确的unique_ptr并调用正确的create函数? -
@NathanOliver 恕我直言,它的扩展性不是很好。这些类型有很多(超过 100 种),而且大多数都有多种创建方式。
-
@Justin 感谢您的信息。这可能并不重要(实际结构比单个
int复杂一点)。我可能必须测量差异以确定,但这是我现在试图避免的大量工作。 -
template<auto f>using deleter_t = std::integral_constant< std::decay_t<decltype(f)>, f >; template<auto f> constexpr deleter_t<f> deleter{};现在unique_ptr<T, deleter_t<destroy>>是您想要的免费删除器。
标签: c++17 c++20 c++ c++17 unique-ptr type-deduction