简体   繁体   English

在C ++容器中作为模板参数提供的allocator和作为构造函数参数提供的allocator之间的区别?

[英]Difference between allocator supplied as template parameter and allocator supplied as constructor argument in C++ containers?

What's the difference between supplying an STL container (for example, std::vector) with an allocator as a template parameter, eg.: 提供STL容器(例如,std :: vector)与分配器作为模板参数之间的区别是什么,例如:

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. 这是不可分割的。

It's like having a function template<typename Type> f(Type instance); 这就像有一个函数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. 并询问Typeinstance之间的区别是什么,它们可以单独使用,还有哪些优点。 It does not make much sense if you do understand what is a template, type and an instance/object. 如果您了解什么是模板,类型和实例/对象,那就没有多大意义。

(for the sake of simplicity it's c++11) (为简单起见,它是c ++ 11)

Here you have type template for vector : 这里有vector类型模板:

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

And here is the default constructor: 这是默认的构造函数:

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

There always is an instance of Allocator provided as alloc parameter. 总是有一个Allocator实例作为alloc参数提供。 All other invocation are similar in this regard. 在这方面,所有其他调用都是类似的。 By default it is default constructed new Allocator object. 默认情况下,它是默认构造的新Allocator对象。 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参数的向量调用时,您就会创建新的Allocator对象(在默认情况下,它很可能什么也不做,但程序的逻辑流程如上所述)。

You cannot pass something that would not fit Allocator because you would get type-mismatch, or precisely in this case a substitution failure. 你不能传递一些不适合Allocator东西,因为你会遇到类型不匹配,或者在这种情况下恰好是替换失败。

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. 在不触及vector定义的情况下,您可以做的一个非常非标准是定义DerivedAllocator ,它派生自Allocator实例化它并作为参数传递。 So for example: 例如:

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

But I am not able to come up with a use-case for such construction on the top of my head. 但是,我无法在我的头顶上提出这种结构的用例。 There is a good use-case, see the addendum below. 有一个很好的用例,请参阅下面的附录。

What is the Allocator template parameter useful for? 什么是Allocator模板参数有用?

In some system you have more than one type of memory, so it might be useful to provide separate allocators (presicely separate allocator types). 在某些系统中,您有多种类型的内存,因此提供单独的分配器(主要是单独的分配器类型)可能很有用。 Eg: SRamAllocator , RamAllocator , etc. 例如: SRamAllocatorRamAllocator等。

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 free ing. 理由是它非常快,因为它没有任何逻辑来跟踪由free引起的“漏洞”块。 You wouldn't want to use it scenarios with heavy new / delete patterns. 您不希望使用具有大量new / delete模式的方案。

What is the allocator constructor parameter useful for? 什么是allocator构造函数参数有用?

It makes sense in case of stateful allocators. 在有状态分配器的情况下它是有意义的。 Imagine you want to have two storages of the same type. 想象一下,你想拥有两个相同类型的存储。 Eg 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. 您可能希望为程序中的每个线程创建一个分配器,因此更容易维护正确的CPU /内存亲和性。

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. 您可以在技术上为每个实例使用不同的类型实现所有内容,但这会降低可能的运行时动态的可用性。

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. 注意:默认分配器和pre-c ++ 11自定义分配器不允许具有状态,因此它们基本上以完全静态的方式实现。 It actually does not matter instance of Allocator you use. 它实际上与您使用的Allocator实例无关。 That is why the default Allocator() works. 这就是默认的Allocator()有效的原因。

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) . 但故意不允许这种方式允许具有内部状态的分配器类型(这句话是个人意见)

IMPORTANT ADDENDUM : I've missed one important perk of c'tor parameter allocator, which is quite possibly it's raison d'être . 重要补充 :我错过了c'tor参数分配器的一个重要特权,这很可能是存在的理由 Polymorphic allocators. 多态分配器。 Is described in detail here: polymorphic_allocator: when and why should I use it? 这里详细介绍: polymorphic_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. 基本上,使用不同的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. 因此,使用参数会产生一些开销,但它会将分配器的状态从铁质标记到类型上,从而减少实现细节。

They actually are exactly the same thing. 他们实际上完全一样的东西。

In the first example, the vector's default constructor default-constructs an allocator of the type you specified. 在第一个示例中,向量的默认构造函数default-构造您指定类型的分配器。

In the second, you provided the allocator yourself; 第二,你自己提供了分配器; it happens to match the default type for the container's allocator. 它碰巧匹配容器的分配器的默认类型。

Both examples make use of default arguments ; 两个示例都使用默认参数 ; one is a default function argument, and the other is a default template argument. 一个是默认函数参数,另一个是默认模板参数。 But the end result in each case is precisely the same. 但每种情况下的最终结果都是一样的。

Here's a demonstrative example: 这是一个示范性的例子:

// N.B. I've included one non-defaulted argument to match
// the vector example, but you could omit `T1` entirely and
// write e.g. `Foo<> obj4`.
template <typename T1, typename T2 = int>
struct Foo
{
   Foo(T2 x = 42) : x(x) {}

private:
   T2 x;
};

int main()
{
   Foo<char, int> obj1;      // like your first example
   Foo<char>      obj2(42);  // like your second example
   Foo<char>      obj3;      // this is more common

   // obj1, obj2 and obj3 are not only of identical type,
   // but also have identical state! You just got there
   // in different ways.
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM