提问人:barsdeveloper 提问时间:2/15/2019 更新时间:2/15/2019 访问量:301
类型擦除上下文中的 C++ 内存分配(使用分配器)
C++ Memory allocation (using allocator) in a type erased context
问:
标准 c++ 库中有许多类可能会分配内存,但不接受分配器。其中一些这样做是因为无法在类型擦除的上下文中分配内存。
一个例子是 std::any,它有一个构造函数,该构造函数在其设计的某个时刻接受 Allocator 参数,但由于似乎无法实现而被丢弃。我确实考虑了一段时间,我想知道阻止其实施的确切问题是什么。标准的哪些要求不能满足?
假设我们从 的基本实现开始。分配内存是微不足道的:any
struct any {
struct type_interface;
template <typename T>
struct type_impl;
type_interface* value;
any(T&& value, const Allocator& allocator = Allocator()) {
using actual_allocator_t
= std::allocator_traits<Allocator>::rebind_alloc<type_impl<T>>;
actual_allocator_t actual_allocator;
// do allocate
// do construct
// assign obtained pointer
}
};
问题显然在于我们丢失了最初分配对象的分配器。一种技巧是创建一个方法,声明一个静态变量来存储该分配器。type_impl<T>
template <typename Allocator>
auto& get_allocator(const Allocator& allocator) {
using actual_allocator_t = std::allocator_traits<Allocator>::rebind_alloc<type_impl<T>>;
// static variable: initialized just on the first call
static actual_allocator_t actual_allocator(allocator);
return actual_allocator;
}
// and the constructor is now
any::any(T&& value, const Allocator& allocator = Allocator()) {
auto actual_allocator = get_allocator(allocator);
// do allocate
// do construct
// assign obtained pointer
}
现在我们可以检索相同的对象来解除分配之前分配的对象。最后一个要解决的问题是释放。对象无法解除分配自身,因此可以使用相同的技巧来包装解除分配逻辑并通过接口使用它。
// Deallocator interface
struct deallocate_interface{
virtual void deallocate(void*) {};
};
template <typename Allocator>
struct deallocate_wrapper{
virtual void deallocate(void* ptr) {
std::allocator_traits<Allocator>::deallocate(
this->allocator,
reinterpret_cast<typename Allocator::value_type*>(ptr),
1u
);
}
};
并且,以同样的方式将其存储到静态方法中:
template <typename Allocator>
deallocate_interface& get_deallocator(Allocator& allocator) {
auto& actual_allocator = get_allocator(allocator);
// static variable: initialized just on the first call
static deallocate_wrapper<std::decay_t<decltype(actual_allocator)>> deallocator(actual_allocator);
return deallocator;
}
我看到的唯一限制是,此实现对相同类型的所有对象使用相同的分配器,这意味着在复制/移动的情况下,分配器不会被复制/移动。但这难道不比没有分配器好吗? 我在这里测试了代码(https://github.com/barsan-md/type-erasure-and-allocation),看看它是否按预期工作。 可能的输出为:
Begin
Allocator: 0x55ec6563a132, allocating: 0x55ec667e4280
Constructed: 0x55ec667e4280, print: Hello
Allocator: 0x55ec6563a132, allocating: 0x55ec667e42b0
Constructed: 0x55ec667e42b0, print: World
Allocator: 0x55ec6563a140, allocating: 0x55ec667e42e0
Constructed: 0x55ec667e42e0, print: 12345
Destroyed: 0x55ec667e42e0, print: 12345
Allocator: 0x55ec6563a140, deallocating: 0x55ec667e42e0
Destroyed: 0x55ec667e42b0, print: World
Allocator: 0x55ec6563a132, deallocating: 0x55ec667e42b0
Destroyed: 0x55ec667e4280, print: Hello
Allocator: 0x55ec6563a132, deallocating: 0x55ec667e4280
End
答: 暂无答案
评论
any
any::type_impl<std::string>
any::type_impl<std::string>
stack_alloc<T>
dynamic_alloc<T>