STL allocator_traits中的静态成员函数的目的是什么?

What is the purpose of the static member functions in STL's allocator_traits?

本文关键字:函数 是什么 静态成员 STL traits allocator      更新时间:2023-10-16

我试图实现一个stl风格的容器类,我有一个关于在我的类中使用分配器的问题:

STL的allocator_traits中静态成员函数的目的是什么?

直到现在,我认为我应该实例化allocator_type(可能通过某种空基优化来改善内存占用)。因此,我将以这样的方式结束:

struct EmptyBaseOpt : allocator_type
{
  EmptyBaseOpt(const allocator_type & a, allocator_type::const_pointer p)
  : allocator_type(a), prefix_ptr(p) { }
  allocator_type::pointer prefix_ptr;
}
EmptyBaseOpt ebo;

然后,我可以按照以下方式使用分配器:

allocator_type & alloc = ebo;
alloc.allocate(100, ebo.prefix_ptr);
另一方面,c++ 11中的allocator_traits似乎暗示了以下用法:
std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);

我猜这个静态allocate成员函数可能会通过其默认构造函数创建allocator_type的临时临时实例。但这导致了以下问题:

  1. 如果allocator_type是一个有状态的分配器会发生什么?如果我使用allocator_traits中的静态成员函数,而不是从allocator_type的实例调用非静态方法,这些分配器是否能够保持它们的状态?

  2. 为什么我应该实例化allocator_type,如果我可以直接使用allocator_traits中的静态成员函数,那么就麻烦像EBO这样的东西?

  3. 如前所述,我的理解是,任何类模板参数都应该在容器类中实例化,以便允许这些参数的有状态版本。这种理解是否正确?它如何与allocator_traits中的静态成员函数相适应?

另一方面,c++ 11中的allocator_traits似乎暗示了以下用法:

std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);

不,你错过了函数调用最重要的参数:分配器。

我猜这个静态分配成员函数可能会通过它的默认构造函数创建一个临时的allocator_type实例。

不能,因为你给函数传递了一个分配器参数。

如果allocator_type是一个有状态的分配器会发生什么?

它工作得很好,因为您将有状态分配器作为参数传递给使用它的函数。

如果我可以直接使用allocator_traits中的静态成员函数,为什么我应该实例化allocator_type,并为像EBO这样的东西而烦恼?

因为你不能用它们代替。

如前所述,我的理解是任何类模板参数都应该在容器类中实例化,以便允许这些参数的有状态版本。这种理解是正确的吗?它如何与allocator_traits中的静态成员函数相适应?

是的,你的理解是正确的,如果你使用得当,它很适合allocator_traits

allocator_traits的目的是为大多数Allocator接口提供合理的默认值。这有两个目的:

  • 首先,在c++ 11中定义分配器更简单(您只需要提供value_type, allocate, deallocate,用于重新绑定分配器和operator==operator!=的模板构造函数),因此编写简单的自定义分配器现在更简单。

  • 其次,它允许仅满足c++ 03分配器要求的现有分配器被c++ 11容器使用。c++ 03分配器没有定义嵌套成员,比如c++ 11容器寻找的propagate_on_container_swap,或者新的可变construct(pointer, Args&&...)成员,它允许用任何参数构造对象,而不仅仅是复制构造(这是允许emplace工作的)。因此,通过在allocator_traits中包装分配器的使用,大多数Allocator接口都被赋予了合理的默认值,这样,使用自定义分配器和容器(如std::vector)的现有c++ 03代码在使用c++ 11重新编译时不会突然构建失败。std::vector实现只通过allocator_traits类型使用新成员,因此自定义分配器没有更新以提供c++ 11 allocator要求的所有新成员并不重要。

您似乎错过了allocator_traits中的所有静态成员函数都将Alloc &作为第一个参数的事实。也就是说,它们使用一个分配器类型的对象来完成它们的工作。

"仅通过allocator_traits访问分配器"策略的原因是,如果分配器类型本身没有提供分配器操作,allocator_traits的一些成员提供了分配器操作的默认实现。这适用于所有在c++ 11中添加到分配器要求中,而在c++ 03中不存在的成员。一个例子是construct,如果分配器类型没有提供合适的construct函数,它将调用放置new运算符::new。因此,这些默认值允许为c++ 03编写的分配器在c++ 11容器中保持不变。

此外,使用allocator_traits允许更多的可定制性。可以为特定的分配器类指定allocator_traits,并且可以通过对Alloc &参数的不同调用来实现trait函数。

所以你对实例化allocator_type的假设是正确的。不同之处在于,您不应该直接调用其成员函数(因为它们可能不存在),而是通过allocator_traits中的静态访问器。