template specialized c++ templates clang language-lawyer template-specialization

specialized template c++



¿Puede el parámetro de plantilla de plantilla variad ser parcial especializado? (1)

Considere el siguiente programa:

template<template<typename ...> class> struct foo {}; template<template<typename> class C> struct foo<C> {}; int main() {}

Clang lo rechaza con error:

La plantilla de clase de especialización parcial no especializa ningún argumento de plantilla.

incluso en la última versión de HEAD 7.0, vea la demostración here . Sin embargo, gcc lo acepta .

Consulte [temp.class.spec] donde se establecen las reglas de especialización parcial, no pude encontrar nada que prohíba la especialización parcial de esta plantilla. Especialmente, la especialización es de hecho más especializada , el mensaje de error parece incorrecto.

EDITAR:

Sin embargo, el comportamiento de gcc también es anormal, considere el siguiente programa:

#include <iostream> template<template<typename ...> class> struct foo { void show() { std::cout << "Primary./n"; } }; template<template<typename> class C> struct foo<C> { void show() { std::cout << "Specialized./n"; } }; template<class...> struct bar {}; int main() { foo<bar> f; f.show(); }

Resulta que gcc usa la versión especializada en este caso, vea here .

Ahora quiero preguntar:

  • ¿Es este tipo de especialización parcial permitido por el estándar?

  • ¿Qué compilador es correcto? (uno / todos / ninguno de ellos?)


Este último parece ser un error de GCC en la implementación de la nueva plantilla de C ++, la deducción de argumentos de la compatibilidad parcial para esta característica .

Para determinar si una especialización parcial es más especializada que la plantilla de clase, el ordenamiento parcial se aplica a 2 funciones sintetizadas correspondientes :

//template class: template<template<class...>class P> void f_foo0(foo<P>); //Partial specialization template<template<class P> class P> void f_foo_partial0(foo<P>);

Luego se intenta realizar una deducción de argumento de plantilla, llamando a cada función con un argumento correspondiente al otro parámetro de función (ver [temp.func.order] )

template<class P> struct Atype{}; template<class ...P> struct ATypePack{}; //Is f_foo_partial at least as specialized as f_foo? f_foo(foo<AType>{}); //Is f_foo at least as specialized as f_foo_partial? f_foo_partial(foo<ATypePack>{});

Si el argumento de la plantilla tiene éxito para (1), f_foo_partial es al menos tan especializado como f_foo . Si el argumento de la plantilla es exitoso para (2), entonces f_foo es al menos tan especializado como f_foo_partial (vea [temp.deduct.partial] ). Entonces, si solo uno es al menos tan especializado como el otro, entonces es el que está más especializado .

Entonces, para verificar si el argumento de la plantilla es deducible , entonces se realiza la deducción del argumento de la plantilla de un tipo .

Luego, para realizar esta comparación, se aplica la regla introducida en C ++ 17 [temp.arg.template]/3 :

Un argumento de plantilla coincide con un parámetro de plantilla de plantilla P cuando P es al menos tan especializado como el argumento de plantilla A. [...]

Y [temp.arg.template] / 4 especifica que este ordenamiento se realizará de manera similar al caso anterior usando estas dos funciones inventadas:

template<class...> struct X{}; //for the argument template<class...P> void f_targ(X<P...>); //Partial specialization template<class P> void f_tparam(X<P>); struct Atype{}; struct ATypePack{}; //Is template template parameter at least as specialized template template arg? f_targ(X<AType>{}); //Is template template arg at least as specialized as template template parameter? f_tparam(X<ATypePack>{});

  • para (1) argumento de la plantilla, el argumento deducido para ...P`` is AType`.

  • para (2) hay una regla especial, que se aplica solo en el caso de la ordenación parcial de la plantilla [temp.deduct.type] /9.2:

[...] Durante el pedido parcial, si Ai era originalmente una expansión de paquete:

  • si P no contiene un argumento de plantilla correspondiente a Ai, entonces Ai se ignora;

  • de lo contrario, si Pi no es una expansión de paquete, la deducción del argumento de la plantilla falla.

Aquí Ai es ATypePack y Pi es la P en el argumento de la función de la template<class P> void p_foo_partial(foo<P>) .

Por lo tanto, debido a esta regla citada en negrita, la deducción de argumentos de plantilla falla para (2), por lo que el parámetro de plantilla de plantilla "clase P" es más especializado que su argumento. Así que la llamada f_foo_partial(foo<ATypePack>{}) está bien formada.

Por otro lado, las mismas reglas, la llamada a f_foo(AType{}) está bien formada debido a [temp.arg.temp] / 3:

[...] Si P contiene un paquete de parámetros, entonces A también coincide con P si cada uno de los parámetros de la plantilla de A coincide con el parámetro de plantilla correspondiente en la lista de parámetros de plantilla de P. [...]

así que f_foo_partial no es más especializado que f_foo , así que la template<class<class > class C> struct foo<C>; No es una especialización parcial de la plantilla foo .