在C++容器中,作为模板参数提供的分配器和作为构造函数参数提供的分配程序之间的区别

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

本文关键字:参数 构造函数 分配 程序 区别 之间 分配器 C++      更新时间:2023-10-16

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

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

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

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

如果它们不是同一件事(即一个提供类型,另一个提供实例),并且可以单独使用,那么它们的优点是什么?

是否可以分别使用

template参数只提供类型。您仍然需要一个实例。它是不可分离的。

这就像有一个函数template<typename Type> f(Type instance);,问Typeinstance之间有什么区别,它们可以单独使用吗?两者的优点是什么。如果您确实了解什么是模板、类型和实例/对象,那么这就没有多大意义。

(为了简单起见,它是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模板参数的作用是什么

在某些系统中,您有不止一种类型的内存,因此提供单独的分配器(预先单独的分配器类型)可能会很有用。例如:SRamAllocatorRamAllocator

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

allocator构造函数参数的作用是什么

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

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

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

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

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

重要附录:我错过了c'tor参数分配器的一个重要好处,很可能是它的存在原因多态分配器这里有详细描述:polymorphic_alloctor:我应该何时以及为什么使用它?

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

它们实际上是完全相同的东西

在第一个示例中,向量的默认构造函数default构造您指定类型的分配器。

在第二个例子中,您自己提供了分配器;它恰好与容器分配器的默认类型相匹配。

这两个例子都使用了默认参数;一个是默认的函数参数,另一个是缺省的模板参数。但每种情况的最终结果都是完全相同的。

下面是一个示例:

// 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.
}