4

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

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

23.1.8

此子句中定义的所有容器类型的复制构造函数从它们各自的第一个参数中复制分配器参数。这些容器类型的所有其他构造函数都采用 Allocator& 参数 (20.1.5),这是一个值类型与容器的值类型相同的分配器。此参数的副本用于在每个容器对象的生命周期内由这些构造函数和所有成员函数执行的任何内存分配。在此子句中定义的所有容器类型中,成员 get_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);

我想,如果人们想在那里实现自己的容器等并有效地利用复制构造,那么这些类型的函数(即使手动实现它们是微不足道的......直到你试图让它们异常安全)将被证明是相当有用的在使用分配器时。

想法?

4

3 回答 3

3

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

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

@MarkB 确定了标准不应该这样做的一个很好的理由(范围不知道容器的分配器)。我什至可以说这只是一个固有的限制。

你总是可以根据你的需要重新发明uninitialized_copy,知道分配器应该是什么。这只是一个两行for循环。

于 2012-03-15T20:45:59.263 回答
1

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

于 2012-03-15T20:34:42.100 回答
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, ...).

于 2020-01-01T04:39:35.770 回答