我正在尝试实现一个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
的临时临时实例。但这导致了以下问题:
如果allocator_type
是有状态分配器,会发生什么?如果我使用allocator_traits
中的静态成员函数而不是从allocator_type
的实例调用非静态方法,这样的分配器是否能够保持其状态?
如果我可以直接使用allocator_type
中的静态成员函数,我为什么要实例化allocator_traits
而烦恼EBO之类的东西?
如前所述,我的理解是,任何类类模板参数都应该在我的容器类中实例化,以便允许这些参数的有状态版本。这种理解是否正确,它如何适应allocator_traits
中的静态成员函数?
答案 0 :(得分:6)
另一方面,C ++ 11中的allocator_traits似乎意味着以下用法:
std::allocator_traits<allocator_type>::allocate(100, ebo.prefix_ptr);
不,你错过了该函数调用最重要的参数:分配器。
我想这个静态分配成员函数可能会通过其默认构造函数创建一个临时的临时实例allocator_type。
不,因为你将一个allocator参数传递给函数。
如果allocator_type是有状态分配器会发生什么?
它工作正常,因为您将有状态分配器作为参数传递给使用它的函数。
如果我可以直接使用allocator_traits中的静态成员函数,为什么我要实例化allocator_type而烦恼EBO这样的东西?
因为你不能使用它们。
如前所述,我的理解是,任何类类模板参数都应该在我的容器类中实例化,以便允许这些参数的有状态版本。这种理解是否正确,它如何适应allocator_traits中的静态成员函数?
是的,您的理解是正确的,如果您正确使用它,它与allocator_traits
完全吻合。
allocator_traits
的要点是为大多数Allocator接口提供合理的默认值。这有两个目的:
首先在C ++ 11中定义一个分配器更简单(你只需要提供value_type
,allocate
,deallocate
,一个用于重新绑定的模板构造函数分配器和operator==
以及operator!=
)所以现在编写简单的自定义分配器要简单得多。
其次,它允许C ++ 11容器使用仅满足C ++ 03分配器要求的现有分配器。 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的所有新成员并不重要。分配器要求。
答案 1 :(得分:5)
您似乎错过了Alloc &
中所有静态成员函数将allocator_traits
作为第一个参数的事实。也就是说,他们使用分配器类型的对象来完成他们的工作。
此策略“仅通过allocator_traits
访问分配器”的原因是construct
的某些成员提供分配器操作的默认实现(如果分配器类型本身未提供)。这适用于在C ++ 11中添加到分配器要求但在C ++ 03中不存在的所有成员。一个示例是new
,如果分配器类型未提供合适的::new
函数,则会调用展示位置construct
运算符allocator_traits
。因此,这些默认值允许为C ++ 03编写的分配器在C ++ 11容器中保持不变。
此外,使用allocator_traits
可以提供更多自定义功能。可以为特定的分配器类指定Alloc &
,并且可以通过allocator_type
参数上的不同调用来实现特征函数。
因此,您对实例化allocator_traits
的假设是正确的。区别在于您不应该直接调用其成员函数(因为它们可能不存在),而是通过<!-- Boostrap Help -->
<html lang="en">
<head>
<title>Example </title>
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/css/bootstrap-theme.min.css">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.3/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>
<style type="text/css">
p {
padding: 50px;
font-size: 32px;
font-weight: bold;
text-align: center;
background: #f2f2f2;
}
#circle {
background: #f00;
width: 50px;
height: 50px;
border-radius: 50%;
}
</style>
</head>
<body>
<div class="container">
<div class="row">
<div class="col-md-12">
<p>Category 1</p>
<div id="circle"></div>
</div>
<div class="col-md-8">
<p>Category 2</p>
</div>
<div class="col-md-4">
<p>Category 3</p>
</div>
<div class="col-md-8">
<p>Category 4</p>
</div>
<div class="col-md-8">
<p>Category 5</p>
</div>
</div>
</div>
</body>
</html>
中的静态访问器。