make_unique 的异常感知 malloc 版本
Exception-aware malloc version of make_unique
在 C++11
中没有 make_unique
(不能使用 C++14
),尽管可以像这样快速模拟(已经有很多答案建议类似这个):
template< typename T, typename ... Args >
std::unique_ptr< T > my_make_unique (Args && ... args) {
return { new T( std::forward< Args >( args )... ) };
}
如果期望的结果是在 typename T
上调用 new
/ delete
,这很好。在我的例子中,这并不是因为我覆盖了全局 new
/ delete
,它在内部将(在方便的地方)使用 unique_ptr
。因此,我使用 malloc
和基于 malloc
的各种包装器和分配器来使 STL 容器在使用站点没有额外膨胀的情况下工作。在出现异常之前,我用这种方法做得很好......
到目前为止,在 this question 中的答案的基础上,我将此作为可能的通用解决方案:
template< typename T >
struct destroy_free {
void operator() (void * p) {
if ( !p ) return;
static_cast< T* >( p )->~T();
free( p );
}
};
template< typename T, typename ... Args >
auto malloc_make_unique (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
if ( auto ptr = malloc( sizeof(T) ) )
return { new ( ptr ) T( std::forward< Args >( args )... ) };
return { nullptr }; // global new would throw bad_alloc here.
}
这似乎没问题,但这里完全忽略了关于 new (ptr) T(...)
如何工作的异常处理,而不是 new T(...)
,以及这如何影响 make_unique
的任何版本,尤其是当使用自定义分配方法。
首先,我知道抛出 std::bad_alloc
是不明智的,但是我相信最小意外原则适用于此,效仿 new
的做法是可以原谅的(即抛出当分配失败而不是返回 nullptr
时)。这就引出了两个问题。
1.用throw std::bad_alloc()
代替return { nullptr }
是否合理?
2。要正确复制new T(...)
的行为,如果构造函数抛出则需要捕获异常,以便立即释放内存并然后重新抛出构造函数异常?
假设两者都是肯定的,下面是正确处理这种情况还是有其他需要考虑的地方?
template< typename T, typename ... Args >
auto malloc_make_unique_v2 (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
if ( auto ptr = malloc( sizeof(T) ) ) try {
return { new ( ptr ) T( std::forward< Args >( args )... ) };
} catch( ... ) { // catch constructor exceptions
// assumed: memory allocated but object not constructed
free( ptr ); // release memory, object was not constructed?
throw; // propagate whatever the exception was during construction?
}
throw std::bad_alloc(); // no memory allocated, throw bad_alloc?
}
编辑 - 请注意,为简单起见,我忽略了对齐。
Assuming yes to both, is the below handling the situation correctly or is there anything else to consider?
我觉得还不错。
有std::allocate_shared which creates shared_ptr with object memory allocated by custom allocator. What you are making is essentially allocate_unique
which does not exist (perhaps, yet) in standard c++. Maybe you could create custom allocator that uses malloc/free and then implement your own make_unique(如果你没有)和allocate_unique
。
在 C++11
中没有 make_unique
(不能使用 C++14
),尽管可以像这样快速模拟(已经有很多答案建议类似这个):
template< typename T, typename ... Args >
std::unique_ptr< T > my_make_unique (Args && ... args) {
return { new T( std::forward< Args >( args )... ) };
}
如果期望的结果是在 typename T
上调用 new
/ delete
,这很好。在我的例子中,这并不是因为我覆盖了全局 new
/ delete
,它在内部将(在方便的地方)使用 unique_ptr
。因此,我使用 malloc
和基于 malloc
的各种包装器和分配器来使 STL 容器在使用站点没有额外膨胀的情况下工作。在出现异常之前,我用这种方法做得很好......
到目前为止,在 this question 中的答案的基础上,我将此作为可能的通用解决方案:
template< typename T >
struct destroy_free {
void operator() (void * p) {
if ( !p ) return;
static_cast< T* >( p )->~T();
free( p );
}
};
template< typename T, typename ... Args >
auto malloc_make_unique (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
if ( auto ptr = malloc( sizeof(T) ) )
return { new ( ptr ) T( std::forward< Args >( args )... ) };
return { nullptr }; // global new would throw bad_alloc here.
}
这似乎没问题,但这里完全忽略了关于 new (ptr) T(...)
如何工作的异常处理,而不是 new T(...)
,以及这如何影响 make_unique
的任何版本,尤其是当使用自定义分配方法。
首先,我知道抛出 std::bad_alloc
是不明智的,但是我相信最小意外原则适用于此,效仿 new
的做法是可以原谅的(即抛出当分配失败而不是返回 nullptr
时)。这就引出了两个问题。
1.用throw std::bad_alloc()
代替return { nullptr }
是否合理?
2。要正确复制new T(...)
的行为,如果构造函数抛出则需要捕获异常,以便立即释放内存并然后重新抛出构造函数异常?
假设两者都是肯定的,下面是正确处理这种情况还是有其他需要考虑的地方?
template< typename T, typename ... Args >
auto malloc_make_unique_v2 (Args && ... args) -> std::unique_ptr< T, destroy_free< T > > {
if ( auto ptr = malloc( sizeof(T) ) ) try {
return { new ( ptr ) T( std::forward< Args >( args )... ) };
} catch( ... ) { // catch constructor exceptions
// assumed: memory allocated but object not constructed
free( ptr ); // release memory, object was not constructed?
throw; // propagate whatever the exception was during construction?
}
throw std::bad_alloc(); // no memory allocated, throw bad_alloc?
}
编辑 - 请注意,为简单起见,我忽略了对齐。
Assuming yes to both, is the below handling the situation correctly or is there anything else to consider?
我觉得还不错。
有std::allocate_shared which creates shared_ptr with object memory allocated by custom allocator. What you are making is essentially allocate_unique
which does not exist (perhaps, yet) in standard c++. Maybe you could create custom allocator that uses malloc/free and then implement your own make_unique(如果你没有)和allocate_unique
。