是uninitialized_copy / fill(首先,在最后,对于dest,A& a)c ++标准中的疏忽?

时间:2012-03-15 20:21:04

标签: c++ standards allocator

我想知道事情是如何运作的,因此深入研究了c ++标准库。前几天发生了一件事。

要求包含器(例如:std::vector<int, std::allocator<int> >)使用为分配指定的分配器。具体而言,标准说:

23.1.8

  

复制此子句副本中定义的所有容器类型的构造函数   来自各自第一个参数的分配器参数。所有   这些容器类型的其他构造器采用Allocator&amp;   参数(20.1.5),一个分配器,其值类型与   容器的值类型。此参数的副本用于任何内存   由这些构造函数和所有成员执行的分配   函数,在每个容器对象的生命周期中。在所有   在本节中定义的容器类型,成员get_allocator()   返回用于构造的Allocator对象的副本   容器

后来在标准中它说(在几个不同的地方,但我会选择一个)这样的事情:

explicit deque(size_type n, const T& value = T(), const Allocator& = Allocator());
  

效果:构造一个具有n个值副本的双端队列,   使用指定的分配器。

好的,等我的问题。

让我们以std::vector为例,这是实现以下内容的自然而有效的方式:

vector<T, A>::vector(const vector& x)

可能看起来像这样:

template <class T, class A>
vector<T, A>::vector(const vector& x)  {
    pointer p = alloc_.allocate(x.size());
    std::uninitialized_copy(x.begin(), x.end(), p);
    first_ = p;
    last_  = p + x.size();
    end_   = p + x.size();
}

具体来说,我们分配一些内存,然后复制构建所有成员。没有打算做new value_type[x.size()]这样的事情,因为默认情况下构造数组只会覆盖它!。

但是,这不会使用分配器来执行复制构建 ...

我可以手动编写一个类似这样的循环:

while(first != last) {
    alloc_.construct(&*dest++, *first++);
}

但这是浪费,它几乎与std::uninitialized_copy完全相同,唯一的区别是使用分配器而不是放置新的。

所以,你会认为这是一个疏忽,标准没有这样的(看似很明显的)一系列功能:

template <class In, class For, class A>
For uninitialized_copy(In first, In last, For dest, A &a);

template <class In, class Size, class For, class A>
For uninitialized_copy_n(In first, Size count, For dest, A &a);

template <class For, class T, class A>
void uninitialized_fill(For first, For last, const T& x, A &a);

template <class For, class Size, class T, class A>
void uninitialized_fill_n(For first, Size count, const T& x, A &a);

我认为这些类型的函数(即使它们很容易手动实现......直到你试图使它们异常安全)如果人们想要实现自己的容器等并且有效地使用它们将证明是非常有用的使用分配器时的复制构造。

思想?

3 个答案:

答案 0 :(得分:2)

我不确定我们是否可以将其称为“疏忽”本身。

不,您无法为这些专用算法提供自己的分配器。但是,标准也没有包含其他内容。

@MarkB确定了标准不应该执行此操作的一个非常好的理由(该范围不知道容器的分配器)。我甚至说它只是一个固有的限制。

您可以随时重新发明uninitialized_copy,了解分配器应该是什么。它只是一个两行for循环。

答案 1 :(得分:1)

如果这些函数是自由函数,我看不出编译器可以检测到分配器不匹配的任何方式,因为迭代器不保留分配器类型。这反过来可能导致各种难以发现的问题。

答案 2 :(得分:0)

是的,我认为这是一个(重大)疏忽,因为有关分配器的信息会丢失。在当前协议中,分配器是唯一知道如何在内存中精确构造对象的分配器。

现在Boost包含alloc_construct, alloc_destroy https://www.boost.org/doc/libs/1_72_0/libs/core/doc/html/core/alloc_construct.html 至少可以帮助实现uninitialized_copy/fill/etc(Alloc a, ...)的通用版本。