具有 ADL 支持的就地make_unique

In-place make_unique with ADL support

本文关键字:make unique ADL 支持 具有      更新时间:2023-10-16

我正在努力更好地学习模板,所以我为就地unique_ptr编写了一个小函数。这是我的代码:

#include <iostream>
#include <array>
#include <memory>
#include <functional>
struct A {
int a = 0;
double b = 0.0;
A() { std::cout << "c1n"; }
A(int a) : a(a) { std::cout << "c2n"; }
~A() { std::cout << "dn"; }
};
template<typename T, typename... Args>
auto make_unique(void *p, Args&&... args) {
return std::unique_ptr<T, std::function<void(T*)>>(
new(p) T(std::forward<Args>(args)...), 
[](T* ptr) { ptr->~T(); });
};
int main() {
std::array<char,50> buf1;
std::array<char,50> buf2;
auto o1 = make_unique<A>(buf1.data());
auto o2 = make_unique<A>(buf2.data(), 3);
}

此代码按我的预期工作,并在对象生存期以就地方式完成时调用构造函数/析构函数。

但这是我的问题。我喜欢将其用作 ADL(依赖于参数的查找(,但是当我将using std::make_unique;添加到代码顶部时,代码编译时出错。发生这种情况似乎是因为编译器无法区分我的make_unique()和库中指定的内容。我想它与此冲突:

template< class T, class... Args >
unique_ptr<T> make_unique( Args&&... args );

我想知道这是否是一种解决此问题的方法? 或者最简单的方法是将我的make_unique()重命名为另一个函数?

std make unique不会以SFINAE/重载友好的方式进行测试,如果它的参数可用于创建对象。 即使有,你的计划也会很糟糕,因为放置新和非放置新不应该依赖于delecate过载选择。 它们是非常不同的操作。

所以有一些改进:

template<class T>
auto placement_unique(void *p) {
return [p](auto&&...args) {
return std::unique_ptr<T, void(*)(T*)>(
::new(p) T(decltype(args)(args)...), 
+[](T* ptr) { ptr->~T(); }
);
};
};

用途是:

auto pa = placement_unique<A>(ptr)( 7 );

与放置 NEW 一样,指针和对象参数是分开的。

这可以进一步改进:

struct destroy_it {
template<class T>
void operator()(T* t)const { if (t) t->~T(); }
};
template<class T>
using unique_placed_ptr = std::unique_ptr<T, destroy_it>;
template<class T>
auto placement_unique(void *p) {
return [p](auto&&...args) {
return unique_placed_ptr<T>(
::new(p) T(decltype(args)(args)...)
);
};
};

这需要比标准unique_ptr多 0 个内存 - 删除程序是无状态的。 它甚至在不同的unique_ptr类型之间很常见。