本文介绍了在 C++ 容器中作为模板参数提供的分配器和作为构造函数参数提供的分配器之间的区别?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

为 STL 容器(例如 std::vector)提供分配器作为模板参数有什么区别,例如:

What's the difference between supplying an STL container (for example, std::vector) with an allocator as a template parameter, eg.:

std::vector<int, std::allocator<int>> some_ints;

并提供一个分配器作为构造函数参数,例如:

and supplying an allocator as a constructor argument, eg:

std::allocator<int> temp;
std::vector<int> some_ints(temp);

考虑到它们不是同一个东西(即一个提供一个类型,另一个提供一个类型实例)并且可以彼此分开使用,那么它们的优点是什么?

and what are the advantages of either, given that they are not the same thing (ie. one supplies a type, the other a type instance) and can be used separately from each other?

推荐答案

可以分开使用吗?

模板参数只提供类型.你仍然需要一个实例.不可分离.

Can be used separately from each other?

The template parameter just supplies the type. You still need an instance. It's not separable.

这就像有一个函数 template<typename Type>f(Type instance); 并询问 Typeinstance 有什么区别,可以单独使用,各自的优点是什么.如果您确实了解什么是模板、类型和实例/对象,这没有多大意义.

It's like having a function template<typename Type> f(Type instance); and asking what is the difference between Type and instance, can they be used separately and what are the advantages of either. It does not make much sense if you do understand what is a template, type and an instance/object.

(为简单起见是 c++11)

这里有 vector 的类型模板:

Here you have type template for vector:

template<
    class T,
    class Allocator = std::allocator<T>
> class vector;

这是默认构造函数:

explicit vector( const Allocator& alloc = Allocator() );

总是有一个 Allocator 的实例作为 alloc 参数提供.在这方面,所有其他调用都是相似的.默认情况下,它是默认构造的新 Allocator 对象.因此,从语义上讲,每当您不使用指定 allocator 参数的向量调用时,您都会创建新的 Allocator 对象(在默认情况下,它很可能什么都不做,但逻辑流程序的描述如前所述).

There always is an instance of Allocator provided as alloc parameter. All other invocation are similar in this regard. By default it is default constructed new Allocator object. So, semantically, whenever you do not use invocation of vector specifying allocator parameter, you do create new Allocator object (which in default case most probably does nothing, but the logical flow of the program is as described).

你不能传递不适合 Allocator 的东西,因为你会得到类型不匹配,或者在这种情况下恰好是替换失败.

You cannot pass something that would not fit Allocator because you would get type-mismatch, or precisely in this case a substitution failure.

在不触及 vector 定义的情况下,您可以做的一个非常非标准的做法是定义派生自 AllocatorDerivedAllocator 实例化它并传递作为论据.比如:

One pretty non-standard you could do without touching the definition of vector is to define DerivedAllocator which derives from Allocator instantiate it and pass as an argument. So for example:

vector<T> v( DerivedAllocator<T>() );

有一个很好的用例,请参阅下面的附录.

There is a good use-case, see the addendum below.

在某些系统中,您拥有不止一种类型的内存,因此提供单独的分配器(确切地说是单独的分配器类型)可能很有用.例如:SRamAllocatorRamAllocator

In some system you have more than one type of memory, so it might be useful to provide separate allocators (presicely separate allocator types). E.g: SRamAllocator, RamAllocator, etc.

这在嵌入式系统中很常见.我知道在某个地方有一个内存模型在实现中实际上并没有释放,当你释放它时它是一个丢失的块.它本质上是一个移动指针.理由是它非常快,因为它没有任何逻辑来跟踪由 freeing 引起的漏洞"块.您不希望在具有大量 new/delete 模式的场景中使用它.

This is quite common in embedded systems. I know that somewhere there there is a memory model in implementation which actually does not free, when you free it it's a lost chunk. It's essentially a moving pointer. The rationale is that it's extremely fast because it does not have any logic to trace blocks of "holes" caused by freeing. You wouldn't want to use it scenarios with heavy new/delete patterns.

在有状态分配器的情况下是有意义的.想象一下,您想拥有两个相同类型的存储.例如.跟踪一些内存使用情况,或者您拥有多个逻辑内存库"的任何原因.您可能希望为程序中的每个线程创建一个分配器,这样更容易维护正确的 CPU/内存关联.

It makes sense in case of stateful allocators. Imagine you want to have two storages of the same type. E.g. to track some memory usage, or whatever reason you come with to have more than one logical "memory banks". You may want to create an allocator for each thread in your program, so it's easier to maintain correct CPU/memory affinity.

当你创建一个新对象时,你需要告诉实例应该由哪个分配器来处理它.

When you create a new object, you need to tell which of the allocators instances should take care of it.

从技术上讲,您可以只为每个实例使用不同的类型来实现所有内容,但这会降低可能的运行时动态的可用性.

You could technically implement everything just using different type for each instance, but that would strip down the usability of possible run-time dynamism.

注意:默认分配器和 c++11 之前的自定义分配器不允许有状态,因此它们基本上是以完全静态的方式实现的.实际上,您使用的分配器实例并不重要.这就是默认 Allocator() 起作用的原因.

NOTE: Default allocator and pre-c++11 custom allocators are disallowed to have a state, so they basically that to be implemented in a fully static way. It actually does not matter instance of Allocator you use. That is why the default Allocator() works.

所以,理论上人们不需要实例化它们,并且可以只使用类型和静态接口......如果标准这么说的话.但是故意不这样做是为了允许分配器类型具有内部状态(这句话是个人意见).

So, theoretically one would no need then to instantiate them, and could work with just type and a static interface... if the standard said so. But it was deliberately not made this way to allow allocator types with an internal state (this sentence is a personal opinion).

重要附录:我错过了 c'tor 参数分配器的一项重要功能,这很可能是 存在的理由.多态分配器.这里有详细描述:polymorphic_allocator:何时以及为什么要使用它?

IMPORTANT ADDENDUM: I've missed one important perk of c'tor parameter allocator, which is quite possibly it's raison d'être. Polymorphic allocators. Is described in detail here: polymorphic_allocator: when and why should I use it?

基本上,使用不同的 Allocator 类型会改变对象的整个类型,因此最终得到的对象基本上是相同的,只是分配器不同.这在某些情况下是非常不希望的.为了避免这种情况,可以编写多态分配器并在类型中使用基分配器,并将具体实现作为运行时参数.因此,可以使用不同的存储引擎拥有完全相同类型的对象.因此使用参数有一些开销,但它减少了分配器的状态,从被烙印到类型上,更多的是一种实现详细.

Basically, using different Allocator type would change the whole type of the object, so one end's up with basically the same object which differ only by allocator. This is under certain circumstances highly undesirable. To avoid it, one can write a polymorphic allocators and use base allocator in the type, and concrete implementations as the runtime parameters. Therefore, one can have object of exactly the same type using different storage engines. So using parameter has some overhead, but it reduces status of the allocator from being iron branded onto the type, to more of an implementational detail.

这篇关于在 C++ 容器中作为模板参数提供的分配器和作为构造函数参数提供的分配器之间的区别?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-01 02:46