std::allocator_traits
能做但 std::allocator
不能的事情假设你要实现一个内存池 MyAllocator
,而 STL 容器默认使用的是 std::allocator
。
如果你希望 STL 容器可以使用你的 MyAllocator
,你 不能直接用 std::allocator
,但可以通过 std::allocator_traits
让你的 MyAllocator
兼容 STL。
#include
#include
#include
template <typename T>
class MyAllocator {
public:
using value_type = T;
T* allocate(std::size_t n) {
std::cout << "Allocating " << n << " objects\n";
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) {
std::cout << "Deallocating objects\n";
::operator delete(p);
}
};
int main() {
std::vector<int, MyAllocator<int>> vec; // 使用自定义分配器
vec.push_back(10); // std::allocator_traits 让 vec 兼容 MyAllocator
vec.push_back(20);
}
✅ std::allocator_traits
使 std::vector
兼容 MyAllocator
,而 std::allocator
无法做到这一点!
rebind
机制:不同类型对象之间的分配在 std::allocator
时代,如果你有一个 Allocator
,但你想用它来分配 double
,你需要手动定义 rebind
:
template <typename T>
struct MyAllocator {
using value_type = T;
template <typename U>
struct rebind { using other = MyAllocator<U>; };
};
问题:
std::allocator
不能直接用于 std::allocator
。rebind
,增加了额外的代码和复杂度。解决方案:std::allocator_traits
自动提供 rebind
template <typename T>
class MyAllocator {
public:
using value_type = T;
T* allocate(std::size_t n) { return static_cast<T*>(::operator new(n * sizeof(T))); }
void deallocate(T* p, std::size_t) { ::operator delete(p); }
};
int main() {
using Alloc = MyAllocator<int>;
using ReboundAlloc = std::allocator_traits<Alloc>::rebind_alloc<double>; // 绑定到double
ReboundAlloc alloc;
double* p = alloc.allocate(5); // 现在可以分配 double 了!
alloc.deallocate(p, 5);
}
✅ std::allocator_traits
自动提供 rebind
,避免手写 rebind
逻辑!
construct
和 destroy
适配自定义指针在 std::allocator
时代,construct()
直接调用 new
,但是如果你有一个 自定义指针(比如智能指针),你就会发现 std::allocator
无法直接适配。
问题:
std::allocator::construct()
只能用于普通指针 T*
,不支持 std::unique_ptr
或 std::shared_ptr
。std::allocator
不支持使用 std::shared_ptr
作为 pointer
类型。解决方案:使用 std::allocator_traits
适配智能指针
#include
#include
template <typename T>
struct SmartAllocator {
using value_type = T;
using pointer = std::unique_ptr<T>; // 使用 unique_ptr 而不是裸指针
T* allocate(std::size_t n) {
return static_cast<T*>(::operator new(n * sizeof(T)));
}
void deallocate(T* p, std::size_t) {
::operator delete(p);
}
};
int main() {
SmartAllocator<int> alloc;
using AllocTraits = std::allocator_traits<SmartAllocator<int>>;
int* p = AllocTraits::allocate(alloc, 1);
AllocTraits::construct(alloc, p, 42);
std::cout << "Constructed value: " << *p << std::endl;
AllocTraits::destroy(alloc, p);
AllocTraits::deallocate(alloc, p, 1);
}
✅ std::allocator_traits
使得 SmartAllocator
可以支持 unique_ptr
,而 std::allocator
无法支持!
constexpr
分配器在现代 C++ 中,某些分配器需要支持 constexpr
,而 std::allocator
不能被 constexpr
调用。但 std::allocator_traits
可以提供 constexpr
支持:
template <typename T>
struct ConstexprAllocator {
using value_type = T;
constexpr T* allocate(std::size_t n) {
return new T[n];
}
constexpr void deallocate(T* p, std::size_t) {
delete[] p;
}
};
constexpr int test() {
ConstexprAllocator<int> alloc;
using AllocTraits = std::allocator_traits<ConstexprAllocator<int>>;
int* p = AllocTraits::allocate(alloc, 1);
AllocTraits::construct(alloc, p, 42);
int val = *p;
AllocTraits::destroy(alloc, p);
AllocTraits::deallocate(alloc, p, 1);
return val;
}
static_assert(test() == 42, "Test failed");
✅ std::allocator_traits
使得 ConstexprAllocator
可以支持 constexpr
,而 std::allocator
无法支持!
std::allocator_traits
相比 std::allocator
的优越性特性 | std::allocator |
std::allocator_traits |
---|---|---|
适配自定义 Allocator |
❌ 不支持 | ✅ 适配 MyAllocator |
支持 rebind 机制 |
❌ 需要手写 rebind |
✅ 自动提供 rebind |
支持智能指针 | ❌ 不支持 | ✅ 可以适配 unique_ptr |
适配 constexpr |
❌ 不能 constexpr |
✅ 支持 constexpr |
统一 STL 分配接口 | ❌ STL 不能通用 | ✅ vector 、map 都能用 |
std::allocator_traits
?Allocator
,并想让 STL 容器使用它。Allocator
中使用 unique_ptr
或 shared_ptr
。Allocator
适用于不同类型的对象(使用 rebind
)。Allocator
在 constexpr
计算时可以工作。虽然 std::allocator
仍然在某些简单场景下可用,但现代 C++ 开发 几乎所有 STL 容器 都依赖 std::allocator_traits
,而 std::allocator
已经不再直接使用。