这个问题比看起来要困难得多。因为分配器类型是对象类型的一部分,所以只有分配器不同的类型之间几乎不允许交互。想到的第一个例子是,一个std::string
接受常量引用的函数不能接受一个使用不同分配器的字符串。一种特殊的情况是在对象的构造过程中。事实上,这可能是更难的情况。
例如,考虑以下代码:
// Assume that you could construct from a different allocator
std::vector<int, allocator1> f() {
std::vector<int, allocator2> r;
// fill in
return r;
}
int main() {
std::vector<int, allocator3> x = f();
}
考虑allocator1
是std::allocator<int>
(即默认分配器),它allocator2
使用堆栈中的本地区域,并且allocator3
可能使用共享内存。理论上代码很简单,r
创建向量并填充数据,在 return 语句中,通过复制from创建一个新的临时对象r
,最后x
通过复制构建从那个暂时的。问题是标准允许(和编译器喜欢)尽可能避免复制。在上面的特定示例中(并忽略分配器),编译器将删除两个副本并仅创建一次缓冲区,这是快速且高效的。但是由于分配器可能不同,因此必须禁用 NRVO 和其他类型的复制省略。(如果启用了这些优化,x
主要将使用allocator2
, 具有已被破坏的本地竞技场,从而导致未定义的行为)。
通过启用从具有一个分配器的容器到另一个分配器的复制构造,您最终可能会陷入混乱,或者比我们在当前标准中已经存在的更严重的混乱,您可能会在有状态分配器中引起各种有趣的问题(假设您使用每个线程分配器,并且您将数据移动到共享队列中,您最终可能会导致一个线程持有由另一个线程上的每个线程分配器创建的对象,并且因为使用每个线程分配器的目的是避免争用锁,您可能会在明显安全的代码上创建竞争条件....
这是向 C++ 委员会提出的一项旧提案Towards a better allocation model,它引发了对 C++03 分配模型的一些担忧,并提出了一种多态分配器类型(它有自己的问题)。它读起来很有趣,但要注意细节,并非一切都像看起来那样好,使用任何一个选项(或类似于 C++03 的 C++11 版本)都有很多缺陷版本)