如何将状态传递给 STL 中的 rebind_alloc 构造函数?
How can I pass a state to the rebind_alloc constructor in STL?
假设我有一个要用于 STL 容器的自定义有状态分配器。容器将使用 rebind_alloc
特性来分配其内部元素,因此默认情况下(或通过定义 rebind<T>::other
),我可以要求它使用我的分配器 class 作为内部元素嗯。
但是,我想传递我的分配器的状态。
此相关question问为什么
template<class U> allocator( const allocator<U> & o ) throw()
存在并且答案表明我想做的是可能的。
但是,根据我的实验(基本上是在自定义分配器中跟踪对 Allocator()
和 template <class U> Allocator(const Allocator<U> &other)
的调用),我无法调用 rebind_alloc
转换构造函数而不是默认构造函数。
其实很简单,如果容器的构造函数被赋予一个分配器实例作为参数,转换构造函数就会被调用。容器的构造函数必须知道分配器实例是合乎逻辑的。
例如,如果没有给容器任何参数,将使用分配器的默认构造函数。
#include <memory>
#include <set>
#include <iostream>
#include <type_traits>
void *default_allocator;
template <typename T>
struct Allocator
{
using value_type = T;
using pointer = T *;
using size_type = size_t;
int state;
Allocator(int s) : state(s)
{
std::cout << "default with arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
Allocator() : Allocator(*static_cast<Allocator *>(default_allocator)) // state(static_cast<Allocator *>(default_allocator)->state)
{
std::cout << "default without arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
template <class U>
Allocator(const Allocator<U> &other)
{
state = other.state;
std::cout
<< "conversion " << typeid(T).name() << " from " << typeid(U).name() << std::endl;
std::cout << "state " << state << std::endl;
}
pointer allocate(size_type n)
{
std::allocator<T> a;
return a.allocate(n);
}
void deallocate(pointer p, size_type n)
{
std::allocator<T> a;
a.deallocate(p, n);
}
};
template <class T, class Compare = std::less<T>>
using set = std::set<T, Compare, Allocator<T>>;
int main()
{
std::cout << "new Allocator<int>(10)" << std::endl;
default_allocator = static_cast<void *>(new Allocator<int>(10));
std::cout << std::endl;
std::cout << "alloc(20)" << std::endl;
Allocator<int> alloc(20);
std::cout << std::endl;
std::cout << "set<int> s1;" << std::endl;
set<int> s1;
std::cout << std::endl;
std::cout << "set<int> s2(alloc);" << std::endl;
set<int> s2(alloc);
}
new Allocator<int>(10)
default with arg i
state 10
alloc(20)
default with arg i
state 20
set<int> s1;
default without arg NSt3__111__tree_nodeIiPvEE
state 10
set<int> s2(alloc);
conversion NSt3__111__tree_nodeIiPvEE from i
state 20
假设我有一个要用于 STL 容器的自定义有状态分配器。容器将使用 rebind_alloc
特性来分配其内部元素,因此默认情况下(或通过定义 rebind<T>::other
),我可以要求它使用我的分配器 class 作为内部元素嗯。
但是,我想传递我的分配器的状态。
此相关question问为什么
template<class U> allocator( const allocator<U> & o ) throw()
存在并且答案表明我想做的是可能的。
但是,根据我的实验(基本上是在自定义分配器中跟踪对 Allocator()
和 template <class U> Allocator(const Allocator<U> &other)
的调用),我无法调用 rebind_alloc
转换构造函数而不是默认构造函数。
其实很简单,如果容器的构造函数被赋予一个分配器实例作为参数,转换构造函数就会被调用。容器的构造函数必须知道分配器实例是合乎逻辑的。
例如,如果没有给容器任何参数,将使用分配器的默认构造函数。
#include <memory>
#include <set>
#include <iostream>
#include <type_traits>
void *default_allocator;
template <typename T>
struct Allocator
{
using value_type = T;
using pointer = T *;
using size_type = size_t;
int state;
Allocator(int s) : state(s)
{
std::cout << "default with arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
Allocator() : Allocator(*static_cast<Allocator *>(default_allocator)) // state(static_cast<Allocator *>(default_allocator)->state)
{
std::cout << "default without arg " << typeid(T).name() << std::endl;
std::cout << "state " << state << std::endl;
}
template <class U>
Allocator(const Allocator<U> &other)
{
state = other.state;
std::cout
<< "conversion " << typeid(T).name() << " from " << typeid(U).name() << std::endl;
std::cout << "state " << state << std::endl;
}
pointer allocate(size_type n)
{
std::allocator<T> a;
return a.allocate(n);
}
void deallocate(pointer p, size_type n)
{
std::allocator<T> a;
a.deallocate(p, n);
}
};
template <class T, class Compare = std::less<T>>
using set = std::set<T, Compare, Allocator<T>>;
int main()
{
std::cout << "new Allocator<int>(10)" << std::endl;
default_allocator = static_cast<void *>(new Allocator<int>(10));
std::cout << std::endl;
std::cout << "alloc(20)" << std::endl;
Allocator<int> alloc(20);
std::cout << std::endl;
std::cout << "set<int> s1;" << std::endl;
set<int> s1;
std::cout << std::endl;
std::cout << "set<int> s2(alloc);" << std::endl;
set<int> s2(alloc);
}
new Allocator<int>(10)
default with arg i
state 10
alloc(20)
default with arg i
state 20
set<int> s1;
default without arg NSt3__111__tree_nodeIiPvEE
state 10
set<int> s2(alloc);
conversion NSt3__111__tree_nodeIiPvEE from i
state 20