¿Por qué es necesario allocator :: rebind cuando tenemos parámetros de plantilla de plantilla?

Cada clase de asignador debe tener una interfaz similar a la siguiente:

template class allocator { ... template struct rebind { typedef allocator other; }; }; 

Y las clases que usan asignadores hacen algo redundante como este:

 template<class T, class Alloc = std::allocator > class vector { ... }; 

Pero, ¿por qué es esto necesario?

En otras palabras, ¿no podrían haber dicho simplemente lo siguiente?

 template class allocator { ... }; template<class T, template class Alloc = std::allocator> class vector { ... }; 

que es a la vez más elegante, menos redundante y (en algunas situaciones similares) potencialmente más seguro?
¿Por qué fueron a la ruta de rebind , que también causa más redundancia (es decir, tienes que decir T dos veces)?

(Una pregunta similar va para char_traits y el rest … aunque no todos tienen rebind , aún podrían beneficiarse de los parámetros de plantilla de plantilla).


Editar:

¡Pero esto no funcionará si necesita más de 1 parámetro de plantilla!

¡En realidad, funciona muy bien!

 template struct pool { template struct allocator { T pool[PoolSize]; ... }; }; 

Ahora si el vector solo se definió de esta manera:

 template<class T, template class Alloc> class vector { ... }; 

Entonces podrías decir:

 typedef vector<int, pool::allocator> int_vector; 

Y funcionaría perfectamente, sin necesidad de que usted (redundantemente) dijera dos veces.

Y una operación de rebind en el vector se convertiría en Alloc lugar de Alloc::template rebind::other .

Un texto citado de Fundamentos de Algoritmos en C ++ 11 , Volumen 1, capítulo 4, p. 35:

 template  struct allocator { template  using rebind = allocator; }; 

uso de muestra:

 allocator::rebind x; 

En The C ++ Programming Language , 4ª edición, sección 34.4.1, p. 998, comentando el miembro de reenlace ‘clásico’ en la clase de asignador predeterminado:

 template struct rebind { using other = allocator;}; 

Bjarne Stroustrup escribe esto:

La curiosa plantilla de reenlace es un alias arcaico. Debería haber sido:

 template using other = allocator; 

Sin embargo, el asignador se definió antes de que dichos alias fueran compatibles con C ++.

Pero, ¿por qué es esto necesario?

¿Qué pasa si su clase de asignador tiene más de un argumento de plantilla?

Eso es más o menos en términos de por qué generalmente se desaconseja usar argumentos de plantilla de plantilla, a favor del uso de argumentos de plantilla normales, incluso si eso significa un poco de redundancia en el sitio de creación de instancias. En muchos casos (sin embargo, probablemente no para asignadores), ese argumento puede no ser siempre una plantilla de clase (por ejemplo, una clase normal con funciones de miembro de plantilla).

Puede que le resulte conveniente (dentro de la implementación de la clase de contenedor) utilizar un parámetro de plantilla de plantilla solo porque simplifica parte de la syntax interna. Sin embargo, si el usuario tiene una plantilla de clase de argumentos múltiples como un asignador que quiere usar, pero necesita que el usuario proporcione un asignador que sea una plantilla de clase de argumento único, lo obligará a crear un contenedor para casi cualquier nuevo contexto en el que deba usar ese asignador. Esto no solo no se puede escalar, sino que también puede ser muy incómodo de hacer. Y, en este punto, esa solución está lejos de ser la solución “elegante y menos redundante” que originalmente pensaste que sería. Supongamos que tiene un asignador con dos argumentos, ¿cuál de los siguientes es el más fácil para el usuario?

 std::vector > v1; std::vector::template type > v2; 

Básicamente obligas al usuario a construir muchas cosas inútiles (envoltorios, alias de plantillas, etc.) solo para satisfacer las demandas de tu implementación. Exigir al autor de una clase de asignador personalizado que suministre una plantilla de reenlace anidada (que es solo un alias de plantilla trivial) es mucho más fácil que todas las contorsiones que requiere con el enfoque alternativo.

En su enfoque, está forzando al asignador a ser una plantilla con un solo parámetro, lo que podría no ser siempre el caso. En muchos casos, los asignadores pueden ser no plantilla, y la rebind anidada puede devolver el mismo tipo de asignador. En otros casos, el asignador puede tener argumentos de plantilla adicionales. Este segundo caso es el caso de std::allocator<> que, como todas las plantillas de la biblioteca estándar, puede tener argumentos de plantilla adicionales, siempre que la implementación proporcione valores predeterminados. También tenga en cuenta que la existencia de rebind es opcional en algunos casos, donde allocator_traits se puede utilizar para obtener el tipo de rebote.

El estándar en realidad menciona que el rebind nested es en realidad un typedef con plantillas:

§17.6.3.5 / 3 Nota A: La reorganización de la plantilla de la clase miembro en la tabla anterior es efectivamente una plantilla typedef. [Nota: en general, si el nombre Allocator está vinculado a SomeAllocator , entonces Allocator::rebind::other es del mismo tipo que SomeAllocator , donde someAllocator::value_type es T y SomeAllocator::value_type es U. – end note] Si Allocator es una instanciación de plantilla de clase de la forma SomeAllocator , donde Args es cero o más argumentos de tipo, y Allocator no proporciona una plantilla de reenlace, la plantilla estándar allocator_traits usa SomeAllocator en lugar de Allocator:: rebind::other de forma predeterminada. Para los tipos de asignador que no son instancias de plantilla del formulario anterior, no se proporciona ningún valor predeterminado.

Supongamos que quiere escribir una función tomando todo tipo de vectores.

Entonces es mucho más conveniente poder escribir

 template  void f (std::vector  vec) { // ... } 

que tener que escribir

 template  class A> void f (std::vector  vec) { // ... } 

En la mayoría de los casos, dicha función no se preocupa por el asignador de todos modos.

Además, tenga en cuenta que no se requiere que los asignadores sean una plantilla. Puede escribir clases separadas para los tipos particulares que deben asignarse.

Una forma aún más conveniente de diseñar asignadores probablemente habría sido

 struct MyAllocator { template  class Core { // allocator code here }; }; 

Entonces habría sido posible escribir

 std::vector  vec; 

en lugar de la expresión un tanto engañosa

 std::vector  > vec; 

No estoy seguro de si se permite utilizar MyAllocator anterior como un asignador después de agregar una MyAllocator , es decir, si la siguiente es una clase de asignador válida:

 struct MyAllocator { template  class Core { // allocator code here }; template  struct rebind { using other=Core; }; }; 
    Intereting Posts