为什么std类型不提供不同于allocator的源的转换构造函数/赋值

时间:2012-06-11 20:08:24

标签: c++ std allocator library-design

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

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

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

2 个答案:

答案 0 :(得分:3)

标准哈希也不允许分配器乐趣 - 它可以散列std::stringstd::wstring但不散列std::basic_string<char, std::char_traits<char>, custom_alloc>。此外,您不能仅使用分配器创建unordered_mapunordered_set - 您还必须提供一个存储桶编号,默认为您无法访问的实现定义的常量,因此您必须有效做些什么。通常情况下,这种支持并不是很好。

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

答案 1 :(得分: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语句中,由r中的复制创建一个新的临时值,最后{ {1}}由该临时文件中的复制构成。问题是标准允许(和编译器)尽可能避免复制。在上面的特定示例中(并忽略分配器),编译器将忽略两个副本并仅创建一次缓冲区,这是快速且有效的。但由于分配器可能存在差异,因此必须禁用NRVO和其他类型的复制。 (如果启用了这些优化,则main中的x将使用x,并且本地竞技场已被破坏,导致未定义的行为。)

通过启用从具有一个分配器的容器到另一个分配器的复制构造,您可能最终陷入混乱,或者比我们在当前标准中已经陷入更深层次的混乱,在那里您可以引起有状态分配器的所有类型的有趣问题(比如说你使用每线程分配器,并且数据移动到共享队列中,你可能最终得到一个线程,其中包含由另一个线程上的每线程分配器创建的对象,并且因为该点使用每线程分配器是避免锁定的争论,你可能会在明显安全的代码上创建竞争条件....


这是对C ++委员会Towards a better allocation model的一个旧提议,它提出了C ++ 03分配模型的一些问题,并提出了一种多态分配器类型(它有自己的问题)。它是一个有趣的阅读,但要注意细节,并不是一切都像看起来那么好,并且使用任一选项(或类似于C ++ 03的C ++ 11版本)存在相当多的陷阱版本)