3

例如,为什么不template< typename Elem, typename Traits, typename Alloc > basic_string { ... }提供:

template< typename OtherAlloc >
basic_string( const basic_string< Elem, Traits, OtherAlloc >& a_Other ) { ... }

实现这样一个尊重两个分配器的转换构造函数似乎相当简单。当前的情况使得在分配器不同的类型之间进行接口变得非常麻烦。

4

2 回答 2

3

标准散列也不允许分配器的乐趣——它可以散列std::stringstd::wstring不能std::basic_string<char, std::char_traits<char>, custom_alloc>。此外,您不能仅使用分配器创建unordered_map或创建 -unordered_set您还必须提供存储桶编号,该编号默认为您无法访问的实现定义的常量,因此您实际上必须编造一些东西。一般来说,支持不是很好。

在我看来,相对简单,没有人提出这样的功能或探索这个使用空间。

于 2012-06-11T20:11:09.137 回答
1

这个问题比看起来要困难得多。因为分配器类型是对象类型的一部分,所以只有分配器不同的类型之间几乎不允许交互。想到的第一个例子是,一个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();
}

考虑allocator1std::allocator<int>(即默认分配器),它allocator2使用堆栈中的本地区域,并且allocator3可能使用共享内存。理论上代码很简单,r创建向量并填充数据,在 return 语句中,通过复制from创建一个新的临时对象r,最后x通过复制构建从那个暂时的。问题是标准允许(和编译器喜欢)尽可能避免复制。在上面的特定示例中(并忽略分配器),编译器将删除两个副本并仅创建一次缓冲区,这是快速且高效的。但是由于分配器可能不同,因此必须禁用 NRVO 和其他类型的复制省略。(如果启用了这些优化,x主要将使用allocator2, 具有已被破坏的本地竞技场,从而导致未定义的行为)。

通过启用从具有一个分配器的容器到另一个分配器的复制构造,您最终可能会陷入混乱,或者比我们在当前标准中已经存在的更严重的混乱,您可能会在有状态分配器中引起各种有趣的问题(假设您使用每个线程分配器,并且您数据移动到共享队列中,您最终可能会导致一个线程持有由另一个线程上的每个线程分配器创建的对象,并且因为使用每个线程分配器的目的是避免争用锁,您可能会在明显安全的代码上创建竞争条件....


这是向 C++ 委员会提出的一项旧提案Towards a better allocation model,它引发了对 C++03 分配模型的一些担忧,并提出了一种多态分配器类型(它有自己的问题)。它读起来很有趣,但要注意细节,并非一切都像看起来那样好,使用任何一个选项(或类似于 C++03 的 C++11 版本)都有很多缺陷版本)

于 2012-06-11T20:55:12.930 回答