34

每个分配器类都必须有一个类似于以下的接口:

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];

        ...
    };
};

现在 ifvector只是这样定义的:

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.

4

4 回答 4

22

引用自C++11 算法基础,第 1 卷,第 4 章,第 10 页的引用文本。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++ 支持此类别名之前定义的。

于 2013-09-08T09:12:44.107 回答
13

但为什么这是必要的?

如果你的分配器类有多个模板参数怎么办?

这就是为什么通常不鼓励使用模板模板参数,而是支持使用普通模板参数,即使这意味着在实例化站点有一点冗余。在许多情况下(但是,可能不适用于分配器),该参数可能并不总是类模板(例如,具有模板成员函数的普通类)。

您可能会发现(在容器类的实现中)使用模板模板参数很方便,因为它简化了一些内部语法。但是,如果用户有一个多参数类模板作为他想要使用的分配器,但您要求用户提供一个分配器,它是一个单参数类模板,您实际上将强制他为几乎他必须使用该分配器的任何新上下文。这不仅不可扩展,而且做起来也很不方便。而且,在这一点上,该解决方案远不是您最初认为的“优雅且较少冗余”的解决方案。假设您有一个带有两个参数的分配器,以下哪个对用户来说最简单?

std::vector<T, my_allocator<T,Arg2> > v1;

std::vector<T, my_allocator_wrapper<Arg2>::template type > v2;

您基本上强迫用户构建许多无用的东西(包装器、模板别名等)以满足您的实现需求。要求自定义分配器类的作者提供嵌套的重新绑定模板(这只是一个微不足道的模板别名)比使用替代方法所需的所有扭曲要容易得多。

于 2012-09-11T04:07:42.177 回答
5

在您的方法中,您强制分配器成为具有单个参数的模板,但情况可能并非总是如此。在很多情况下,分配器可以是非模板的,嵌套的rebind可以返回相同类型的分配器。在其他情况下,分配器可以有额外的模板参数。第二种情况是 std::allocator<>,只要实现提供默认值,标准库中的所有模板都可以有额外的模板参数。还要注意,rebind在某些情况下,存在是可选的,allocator_traits可以使用 where 来获取回弹类型。

该标准实际上提到嵌套rebind实际上只是一个模板化的 typedef:

§17.6.3.5/3 注意 A:上表中的成员类模板重新绑定实际上是一个 typedef 模板。[注意:通常,如果名称 Allocator 绑定到SomeAllocator<T>,则 Allocator::rebind<U>::other与 类型相同SomeAllocator<U>,其中 someAllocator<T>::value_typeT 和SomeAllocator<U>::value_typeU.-结束注释] 如果 Allocator 是形式的类模板实例化SomeAllocator<T, Args>,其中 Args 是零个或多个类型参数,并且 Allocator 不提供重新绑定成员模板,默认情况下使用标准 allocator_traits 模板SomeAllocator<U, Args>代替Allocator:: rebind<U>::other。对于不是上述形式的模板实例化的分配器类型,不提供默认值。

于 2012-09-11T03:53:27.913 回答
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>; };
};
于 2015-08-02T11:00:55.313 回答