本文是小编为大家收集整理的关于在C++容器中作为模板参数提供的分配器和作为构造参数提供的分配器之间的区别?的处理/解决方法,可以参考本文帮助大家快速定位并解决问题,中文翻译不准确的可切换到English标签页查看源文。
问题描述
用作为模板参数提供STL容器(例如,std :: vector)有什么区别,例如:
std::vector<int, std::allocator<int>> some_ints;
并提供分配器作为构造函数参数,例如:
std::allocator<int> temp; std::vector<int> some_ints(temp);
,鉴于它们不是同一件事(即一个类型,另一个类型实例),并且可以彼此分开使用?
,这是什么优点?推荐答案
可以彼此分开使用?
模板参数仅提供类型.您仍然需要一个实例.它不是可分离的.
这就像拥有一个函数template<typename Type> f(Type instance);并询问Type和instance之间的区别是什么,可以单独使用它们,这是什么优点.如果您了解什么是模板,类型和实例/对象,那就没有多大意义.
(为了简单起见,它是C ++ 11)
在这里您有vector的类型模板:
template< class T, class Allocator = std::allocator<T> > class vector;
这是默认构造函数:
explicit vector( const Allocator& alloc = Allocator() );
总有一个Allocator的实例,如alloc参数.在这方面,所有其他调用都是相似的.默认情况下是默认构造的新Allocator对象.因此,从语义上讲,每当您不使用向量指定allocator参数的调用时,您都会创建新的Allocator对象(在默认情况下很可能无能为力,但是程序的逻辑流如下所述).
您无法通过不适合Allocator的东西,因为您会得到类型 - 匹配,或者在这种情况下是替换失败.
您可以做的一个非常非标准的情况,而无需触摸vector的定义是定义DerivedAllocator,它源自Allocator实例化并作为参数传递.因此:
vector<T> v( DerivedAllocator<T>() );
,但我无法在我的头顶上提出用于这种结构的用例.有一个很好的用例,请参见下面的附录.
什么是Allocator模板参数对?
有用在某些系统中,您有多种内存类型,因此提供单独的分配器(当前分开的分配器类型)可能很有用.例如:SRamAllocator,RamAllocator,等等.
这在嵌入式系统中很常见.我知道某个地方有一个内存模型,实际上是没有免费的,当您释放它时,它是一个丢失的块.从本质上讲,这是一个移动的指针.理由是它非常快,因为它没有任何逻辑来追踪由free> ing引起的"孔"的块.您不想将其与重型new/delete模式使用.
allocator构造函数参数对?
有用在有状态分配的情况下,这很有意义.想象一下,您想拥有两个相同类型的仓库.例如.跟踪某些内存使用情况,或者您带来的任何原因都拥有一个以上的逻辑"内存库".您可能需要为程序中的每个线程创建分配器,因此更容易保持正确的CPU/内存亲和力.
创建一个新对象时,您需要说出哪个分配器实例应该照顾它.
您可以在技术上仅使用不同类型的所有类型来实现所有内容,但这将降低可能的运行时动态的可用性.
注意:默认分配器和PER-C ++ 11自定义分配器不允许具有状态,因此基本上它们要以完全静态的方式实现.实际上,您使用的分配器实例无关紧要.这就是为什么默认Allocator()工作的原因.
因此,从理论上讲,无需进行实例化,并且可以使用类型和静态界面工作……如果标准说的话.但是,故意没有这种方式允许具有内部状态的分配类型(该句子是个人意见).
重要的附录:我错过了一个重要的参数分配器,这很可能是 raisond'être. 多态分配器. "> polymorphic_allocator:我何时以及为什么要使用它?
基本上,使用不同的Allocator类型会更改对象的整个类型,因此,一个端的基本上是相同的对象,而对象仅由分配器差异.在某些情况下,这是高度不良的.为了避免这种情况,可以编写多态性分配器并在类型中使用基本分配器,并将实现作为运行时参数. 因此,一个人可以使用不同的存储引擎具有完全相同类型的对象.因此,使用参数具有一定的开销,但是它使分配器的状态从将铁品牌置于类型上,而不是实现细节.
其他推荐答案
他们实际上是完全相同的东西.
在第一个示例中,向量的默认构造函数默认构造是您指定的类型的分配器.
在第二个,您亲自提供了分配器;它恰好与容器分配器的默认类型匹配.
两个示例都使用默认参数;一个是默认函数参数,另一个是默认模板参数.但是每种情况下的最终结果完全相同.
这是一个示例:
// 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. }
问题描述
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.
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.
(for the sake of simplicity it's c++11)
Here you have type template for 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. 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).
You cannot pass something that would not fit Allocator because you would get type-mismatch, or precisely in this case a substitution failure.
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>() );
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?
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.
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.
What is the allocator constructor parameter useful for?
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.
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).
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?
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.
其他推荐答案
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.
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. }