Asignador de cada clase debe tener una interfaz similar a la siguiente:

template<class T>
class allocator
{
    ...
    template<class Other>
    struct rebind { typedef allocator<Other> other; };
};

Y clases que uso asignadores de hacer algo redundante como este:

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

Pero ¿por qué es esto necesario?

En otras palabras, no podían haber dicho:

template<class T>
class allocator { ... };

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

que es más elegante, menos redundante, y (en algunas situaciones similares) potencialmente más seguro?

¿Por qué van a la rebind ruta, que también es la causa de más de redundancia (es decir, usted tiene que decir T dos veces)?

(Pregunta Similar va a char_traits y el resto… a pesar de que no todos tienen rebind, todavía podría beneficiarse de la plantilla de los parámetros de la plantilla.)


Edit:

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

En realidad, funciona muy bien!

template<unsigned int PoolSize>
struct pool
{
    template<class T>
    struct allocator
    {
        T pool[PoolSize];

        ...
    };
};

Ahora si vector fue sólo define de esta manera:

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

A continuación, usted pudiera decir:

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

Y funcionaría perfectamente bien, sin necesidad de (redundante) decir int dos veces.

Y un rebind operación dentro de vector acaba de convertirse en Alloc<Other> en lugar de Alloc::template rebind<Other>::other.

  • Tenga en cuenta que en C++11, el requisito es relajado y std::allocator_traits<SomeAllocator<T, Args...>>::rebind_alloc<U> es SomeAllocator<U, Args...> como un default si SomeAllocator no proporciona rebind.
  • Para el último punto en la edit: lo feo que la operación de reenlace ve en el interior del vector de la aplicación es irrelevante. Usted, el ejecutor, tiene la carga de hacer las cosas fáciles para el usuario, incluso si esto significa que muy feo y complicado de código bajo el capó. Si usted puede enterrar la fealdad en la aplicación para salir de una interfaz más limpia, es su trabajo para hacerlo.
  • Seguro, pero es incluso más fácil para el usuario? (¿Cómo es eso? Ejemplos o comparaciones sería útil! :D)
  • Uh, sólo se especializan… template<class> class my_special_allocator; template<> class my_special_allocator<my_type> { ... }; y, a continuación, pasar my_special_allocator. Dado que rebindaplicación muy probable que necesite una plantilla de todos modos, deberás evitar esto con el fin de que sea un problema. Pero si usted insiste en no usar las plantillas a todos, bueno, entonces te estás perdiendo el punto… la «T» en STL está ahí por una razón! Y AFAIK este tipo de uso es el punto entero de la plantilla de los parámetros de la plantilla en el primer lugar… ¿verdad?
  • La verdad puede ser decepcionante. La plantilla de reenlace lenguaje sólo puede haber sido más fácil de implementar con mayor compiladores. He encontrado la plantilla plantilla argumento pasa solamente en las nuevas STL código. Así que no es que los ejecutores no sólo como plantilla argumentos de plantilla en general. Lo que personalmente me gusta de la plantilla la plantilla de los argumentos es que una intención específica ya es visible en el nivel de la interfaz después de sólo sintáctica de análisis, es decir, pasar de un tipo de estrategia para los internos privados de uso genérico.
  • Y si pool<1>::allocator<char>::rebind<int>::other necesitan ser pool<4>::allocator<int>.

InformationsquelleAutor Mehrdad | 2012-09-11

4 Comentarios

  1. 18

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

    template <typename T> 
    struct allocator 
    {  
       template <typename U>  
       using  rebind = allocator<U>; 
    }; 

    el ejemplo de uso :

    allocator<int>::rebind<char> x;

    En El Lenguaje de Programación C++, 4ª edición, sección 34.4.1, p. 998, comentando el ‘clásico’ volver a enlazar los estados en mora asignador de clase :

    template<typename U>
         struct rebind { using other = allocator<U>;};

    Bjarne Stroustrup escribe esto:

    La curiosidad de volver a enlazar plantilla es una arcaica de alias. Debería haber sido:

    template<typename U>
    using other = allocator<U>;

    Sin embargo, asignador fue definido antes de alias de este tipo fueron apoyados por C++.

    • +1 para citar stroustrup, por la forma en que su enlace está roto
  2. 10

    Pero ¿por qué es esto necesario?

    Lo que si el asignador de clase tiene más de un argumento de plantilla?

    Que es bastante mucho en términos de por qué es generalmente se desaconseja el uso de la plantilla la plantilla de argumentos, en favor del uso de la plantilla normal argumentos, incluso si esto significa un bit de redundancia en la creación de instancias de sitio. En muchos casos (y sin embargo, probablemente no para asignadores), ese argumento no siempre puede ser 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) para utilizar una plantilla plantilla parámetro sólo porque simplifica parte de la sintaxis interna. Sin embargo, si el usuario tiene un multi-argumento de la plantilla de clase como un asignador lo quiere usar, pero requiere que el usuario proporcione un asignador que es de un solo argumento de plantilla de clase, que, en efecto, la fuerza de él para crear un contenedor para casi cualquier contexto en el que se debe utilizar asignador. Esto no sólo unscalable, también puede llegar a ser muy incómodo que hacer. Y, en este punto, que la solución está lejos de ser el «elegante y menos redundante» la solución que en un principio pensaba que sería. Dicen que había un asignador con dos argumentos, cuál de los siguientes es el más fácil para el usuario?

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

    Básicamente obligar al usuario a construir un montón de cosas inútiles (envoltorios, plantilla alias, etc.) sólo para satisfacer su aplicación demandas. Que requieren el autor de una costumbre de asignación de clase para el suministro de un entramado de reenlace de la plantilla (que es sólo una trivial plantilla alias) es mucho más fácil que todas las contorsiones que requieren con el enfoque alternativo.

    • «¿Qué pasa si su asignador de clase tiene más de un argumento de plantilla?» ¿template<class P1, class P2> struct my_allocator_with_args { template<class T> my_allocator { ... }; };… ahora que acaba de pasar alrededor de my_allocator_with_args<Foo, Bar>::my_allocator como una plantilla parámetro de plantilla.
    • Por cierto, esta respuesta es un duplicado de la respuesta de David. (También es una víctima de alguien más la respuesta, pero se elimina muy rápidamente).
    • ver ediciones.
    • Gracias, ahora a ver mi ediciones. 😉 Si usted todavía piensa que es la peor opción, podría usted por favor, edita tu respuesta para incluir el «unscalable» caso, y cuál es el equivalente de «escalable» versión sería cuando estamos usando rebind lugar? Creo que uno de nosotros es que falta algo. 🙂
    • Por cierto, después de terminar de subir con un ejemplo 😉 hay otra pregunta: si este no era el tipo de problema de la plantilla de los parámetros de la plantilla fueron destinados a resolver, entonces, ¿cuál es su propósito en el primer lugar?
  3. 4

    En su enfoque se está forzando el asignador de ser una plantilla con un solo parámetro, que puede no ser siempre el caso. En muchos casos, asignadores puede ser distinto de la plantilla, y el anidado rebind puede devolver el mismo tipo de el asignador. En otros casos, el asignador puede tener más argumentos de plantilla. Este segundo caso es el caso de std::allocator<> que como todas las plantillas en la biblioteca estándar se le permite tener más argumentos de plantilla tan larga como la aplicación proporciona valores por defecto. También tenga en cuenta que la existencia de rebind es opcional en algunos casos, donde allocator_traits puede ser utilizado para obtener el tipo de rebote.

    El estándar de hecho, se menciona que el anidada rebind en realidad es simplemente una plantilla typedef:

    §17.6.3.5/3
    Nota: El miembro de la plantilla de clase para volver a enlazar en la tabla de arriba es
    efectivamente, un typedef de la plantilla. [ Nota: En general, si el nombre de
    Asignador está obligado a SomeAllocator<T>, entonces
    Allocator::rebind<U>::other es del mismo tipo que SomeAllocator<U>,
    donde someAllocator<T>::value_type es T y SomeAllocator<U>::value_type es U. — final de la nota ] Si Asignador es una plantilla de clase
    la creación de instancias de la forma SomeAllocator<T, Args>, donde Args es cero
    o más tipo de argumentos, y Asignador de no suministro de un miembro de reenlace
    de la plantilla, el estándar allocator_traits plantilla utiliza SomeAllocator<U, Args> en lugar de Allocator:: rebind<U>::other por defecto. Para
    asignación de tipos que no son de plantilla de las instancias de la anterior
    forma, no existe ningún valor predeterminado proporcionado.

    • Por lo que puedo ver, el número de parámetros de la plantilla no podría haber sido posiblemente un problema, si usted necesita un número diferente de parámetros a continuación, puede simplemente hacer un envoltorio que sólo necesita 1 parámetro, que es bastante fácil y más seguro/menos redundante que tener rebind, ¿verdad?
    • necesidad de crear a contenedor, en realidad más como N contenedores, posiblemente muchos de ellos, uno para cada posible combinación de todos los demás argumentos que se utilizan en el programa, donde en cada uno de los contenedor todos los demás argumentos sería enlazado para algún valor
    • No entiendo por qué dicen que N… podría usted comentar/dar un ejemplo?
    • Dada una plantilla asignador que toma dos argumentos, T y Arg, ¿cómo puede usted crear una sola argumento de contenedor con un argumento T para todos los valores posibles de Arg?
    • Uh, plantilla anidada clases? El exterior se lleva a Arg, el interior de una toma T. Que pasan por el interior de uno como una plantilla parámetro de plantilla… consigue los mismos objetivos exactos como los de antes, le da el número de la plantilla de los argumentos que usted necesita, pero todavía funciona de la forma que usted necesita. template<class P1, class P2> struct my_allocator_with_args { template<class T> my_allocator { ... }; };… ahora que acaba de pasar alrededor de my_allocator_with_args<Foo, Bar>::my_allocator como una plantilla parámetro de plantilla. No veo donde está el N viene en.
    • Sería todo más sencillo de lo que la norma exige ahora? (teniendo en cuenta que rebind no es necesario incluso en la mayoría de los casos?) Yo podría ser un poco más simple en el caso simple: un solo argumento de plantilla, a costa de una mayor complejidad cuando hay más argumentos de plantilla. No es imposible, pero yo no veo que usted podría estar ganando mucho, y yo no ver de inmediato cómo se puede aplicar allocator_traits para evitar que requieren el costo extra en el programador de lado (en la versión actual hay pocos casos en los que usted necesita para escribir rebind)
    • (Por CIERTO, N proviene de no tener aunque mucho más en la solución, y la implementación trivial sería un acabado único argumento de la plantilla :))
    • Ahora que hemos establecido es 1 y no N… «Tendría que ser más fácil de lo que la norma exige ahora?» Que definitivamente sería más simple en un mucho de los casos, es decir, donde simplemente no necesita más parámetros (de hecho, yo diría que nunca es más complicado, porque rebind obliga a repetir toda la plantilla args), y sería siempre menos redundantes. Por otra parte, parece como este es el exactamente el tipo de problema que la plantilla de los parámetros de la plantilla se pretende resolver en primer lugar, así que ¿por qué evitarlos?
    • No estoy seguro de que usted entiende que rebind no es un requisito, sino más bien un herramientas para permitir una mayor flexibilidad permitiendo asignadores de donde no puede/no quiere conformarse con lo que allocator_traits hace para reasignar el asignador. También tenga en cuenta que el número de pulsaciones que no es una medida de la complejidad, rebind puede ser necesario repetir todos los argumentos, pero es conceptualmente simple de tener que proporcionar dos niveles de clases de plantilla donde el exterior es de nivel no sólo para solucionar algunos de los argumentos (en forma similar a lo que std::bind hace por los argumentos de la función)
    • Sí, me da el punto, usted está familiarizado con C++11. Gran. Pero no estoy seguro de que usted entiende que allocator_traits incluso no existe cuando rebind fue inventado… ¿por qué sigues en la educación de un C++11-función sólo cuando es descaradamente irrelevante para la pregunta?
    • Estoy familiarizado con C++03 y C++11, de hecho, más familiarizado con C++03. Yo no estoy tan familiarizado con los asignadores aunque, como en C++03 el asignador modelo es muy limitado y tenemos nuestro propio (no conformes) estándar de la biblioteca que implementa la Lakos asignador de modelo. He mirado el C++03 estándar y no hay allocator_traits.
    • … en cualquier caso, parece que se han fijado en rebind como algo a eliminar por su complejidad (en realidad, tener una mirada en ella: una simple plantilla anidada con una sola typedef, lo complejo que es eso?). ¿De verdad creen que my_alloc_with_args<P,Q>::allocator es mejor que my_alloc? Tenga en cuenta que la plantilla exterior es vacío, presente sólo como un hack para inyectar los argumentos en la plantilla anidada. El nombre de la plantilla exterior es engañosa, my_alloc_with_args no es un asignador es un argumento cuaderno sólo para coincidir con una interfaz. ¿Usted realmente encontrar el mejor?
    • Sí, porque es menos redundante y seguros — ¿no habéis leído este? Me puedes decir como plantilla de los parámetros de la plantilla se supone que para ser utilizado, si este no era el caso de uso?
    • He leído que, y el libro de Alexandrescu, un par de veces en realidad. Como ya he mencionado, se utiliza una forma ligeramente diferente de asignador, pero en cualquier caso, en nuestro caso el asignador es no de plantilla (el tipo, la allocate/deallocate funciones miembro son). El mismo enfoque puede ser tomado con el estándar de asignadores en C++03. Me pregunto, ¿cómo muchos de los casos hay con ninguno, 1, múltiples argumentos de plantilla. También tenga en cuenta que los riesgos potenciales que seguros intenta lograr son fácilmente detectables en tiempo de compilación, y por lo tanto no es mucho de un problema…
    • … si el argumento de plantilla que se ha pasado es malo, la firma de allocate no coinciden y el compilador se quejará [no recuerdo exactamente caso de uso en MC++D, y de si se aplican o no]. No me parece que mucho más seguro. Y tener que extraer los parámetros para un adjuntando plantilla sólo se ve horrible (opinión). De nuevo, trate de documento en una sola frase lo que el exterior y el interior de las plantillas con su contenedor son, tratar de explicar a alguien que está aprendiendo por qué usted debe tener un vacío exterior de la plantilla, cuyo único objetivo es la celebración de una plantilla interna de la función.
    • … En relación con el uso de la plantilla de los parámetros de la plantilla, nunca he encontrado un uso para ellos, es decir, algo que no hizo más sentido en mi mente/diseño sin una plantilla parámetro de plantilla.

  4. 0

    Supongamos que queremos escribir una función que recibe todo tipo de vectores.

    Entonces es mucho más conveniente ser capaz de escribir

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

    de tener que escribir

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

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

    Más nota que asignadores no están obligados a ser una plantilla. Podría escribir clases separadas para tipos particulares que deben ser asignados.

    Incluso de una manera más cómoda de diseño de asignadores probablemente habría sido

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

    Entonces habría sido posible escribir

    std::vector <int, MyAllocator> vec;

    lugar de algo engañosa expresión

    std::vector <int, MyAllocator<int> > vec;

    No estoy seguro de si el MyAllocator está permitido para ser utilizado como un asignador después de la adición de un rebind, es decir, si el siguiente es válido asignador de clase:

    struct MyAllocator { 
       template <class T>
       class Core {
          //allocator code here
       };
    
       template <class T>
       struct rebind { using other=Core<T>; };
    };
    • JhonB, ¿por qué no using other = core<T> sin struct?

Dejar respuesta

Please enter your comment!
Please enter your name here