每个allocator类必须具有类似于以下内容的接口:
template<class T>
class allocator
{
...
template<class Other>
struct rebind { typedef allocator<Other> other; };
};
使用分配器的类做了多余的事情:
template<class T, class Alloc = std::allocator<T> >
class vector { ... };
但为什么这有必要呢?
换句话说,他们不能刚才说:
template<class T>
class allocator { ... };
template<class T, template<class> class Alloc = std::allocator>
class vector { ... };
哪个更优雅,更少冗余,(在某些类似的情况下)可能更安全?
为什么他们走rebind
路线,这也导致更多的冗余(即你必须说T
两次)?
(类似的问题转到char_traits
,其余的......虽然它们都没有rebind
,但它们仍然可以从模板模板参数中受益。)
但是如果您需要多于1个模板参数,这将不起作用!
实际上,效果非常好!
template<unsigned int PoolSize>
struct pool
{
template<class T>
struct allocator
{
T pool[PoolSize];
...
};
};
现在,如果vector
仅以这种方式定义:
template<class T, template<class> class Alloc>
class vector { ... };
然后你可以说:
typedef vector<int, pool<1>::allocator> int_vector;
它会很好地工作,没有需要你(冗余地)说int
两次。
rebind
内的vector
操作只会变为Alloc<Other>
而不是Alloc::template rebind<Other>::other
。
答案 0 :(得分:18)
来自 Foundations of Algorithms in C++11 的引用文字,第1卷,第4章,p。 35:
template <typename T>
struct allocator
{
template <typename U>
using rebind = allocator<U>;
};
样本用法:
allocator<int>::rebind<char> x;
C ++编程语言,第4版,第34.4.1节,p。 998,在默认分配器类中评论'经典'重新绑定成员:
template<typename U>
struct rebind { using other = allocator<U>;};
Bjarne Stroustrup写道:
好奇的重新绑定模板是一个古老的别名。应该是:
template<typename U> using other = allocator<U>;
但是,在C ++支持这些别名之前定义了分配器。
答案 1 :(得分:10)
但为什么这有必要呢?
如果你的allocator类有多个模板参数怎么办?
这就是为什么通常不鼓励使用模板模板参数,而不是使用普通模板参数,即使它意味着在实例化站点有点冗余。在许多情况下(但可能不适用于分配器),该参数可能并不总是类模板(例如,具有模板成员函数的普通类)。
您可能会发现使用模板模板参数很方便(在容器类的实现中),因为它简化了一些内部语法。但是,如果用户有一个多参数类模板作为他想要使用的分配器,但是你需要用户提供一个单参数类模板的分配器,你实际上会迫使他为几乎创建一个包装器。他必须使用该分配器的任何新环境。这不仅不可扩展,它也可能变得非常不方便。而且,在这一点上,该解决方案远不是您最初认为的“优雅且不那么多余”的解决方案。假设您有一个带有两个参数的分配器,以下哪个对用户来说最简单?
std::vector<T, my_allocator<T,Arg2> > v1;
std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;
你基本上强迫用户构建很多无用的东西(包装器,模板别名等)只是为了满足你的实现需求。要求自定义分配器类的作者提供嵌套的重新绑定模板(这只是一个简单的模板别名)比使用替代方法所需的所有扭曲要容易得多。
答案 2 :(得分:4)
在您的方法中,您强制分配器成为具有单个参数的模板,而这可能并非总是如此。在许多情况下,分配器可以是非模板的,嵌套的rebind
可以返回相同类型的分配器。在其他情况下,分配器可以有额外的模板参数。第二种情况是std::allocator<>
的情况,因为只要实现提供了默认值,标准库中的所有模板都允许有额外的模板参数。另请注意,rebind
的存在在某些情况下是可选的,其中allocator_traits
可用于获取反弹类型。
标准实际上提到嵌套的rebind
实际上只是一个模板化的typedef:
§17.6.3.5/ 3 注意A:上表中的成员类模板重新绑定是 实际上是一个typedef模板。 [注意:一般来说,如果名称 然后,分配器绑定到
SomeAllocator<T>
Allocator::rebind<U>::other
与SomeAllocator<U>
的类型相同, 其中someAllocator<T>::value_type
为T且SomeAllocator<U>::value_type
为U. - 尾注]如果Allocator是类模板 形式SomeAllocator<T, Args>
的实例化,其中Args为零 或更多类型参数,并且Allocator不提供重新绑定成员 模板,标准allocator_traits模板默认使用SomeAllocator<U, Args>
代替Allocator:: rebind<U>::other
。对于 不是上述模板实例的分配器类型 表格,没有提供默认值。
答案 3 :(得分:0)
假设您想要编写一个采用各种向量的函数。
然后能够写
更方便template <class T, class A>
void f (std::vector <T, A> vec) {
// ...
}
比必须写
template <class T, template <class> class A>
void f (std::vector <T, A> vec) {
// ...
}
在大多数情况下,这样的功能无论如何都不关心分配器。
进一步注意,分配器不需要是模板。您可以为需要分配的特定类型编写单独的类。
设计分配器的更方便的方法可能是
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
};
然后就可以写
了std::vector <int, MyAllocator> vec;
而不是有些误导的表达
std::vector <int, MyAllocator<int> > vec;
我不确定在添加MyAllocator
之后是否允许将上述rebind
用作分配器,即以下是否是有效的分配器类:
struct MyAllocator {
template <class T>
class Core {
// allocator code here
};
template <class T>
struct rebind { using other=Core<T>; };
};