c++ - Plantilla de clase especialización prioridad/ambigüedad
c++11 language-lawyer (1)
Desde temp.class.order :
Para las especializaciones parciales de dos plantillas de clase, la primera es más especializada que la segunda si, dada la siguiente reescritura de dos plantillas de función, la primera plantilla de función es más especializada que la segunda según las reglas de ordenación de las plantillas de función (
[temp.func.order]
):
Cada una de las dos plantillas de función tiene los mismos parámetros de plantilla que la especialización parcial correspondiente.
Cada plantilla de función tiene un único parámetro de función cuyo tipo es una especialización de plantilla de clase donde los argumentos de plantilla son los parámetros de plantilla correspondientes de la plantilla de función para cada argumento de plantilla en la lista de argumentos de plantilla de la identificación de plantilla simple de la especialización parcial .
El orden de:
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I''m more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I''m variadic!" << std::endl;
}
};
depende del orden de
template <template <typename...> class Z, typename T>
void bar(test<Z, Z<T>>); // #1
template <template <typename...> class Z, typename T, typename ... Args>
void bar(test<Z, Z<T, Args...>>); // #2
Desde [temp.func.order]
:
El ordenamiento parcial selecciona cuál de las dos plantillas de función es más especializada que la otra transformando cada plantilla por turno (ver el párrafo siguiente) y realizando la deducción de argumentos de la plantilla usando el tipo de función. El proceso de deducción determina si una de las plantillas es más especializada que la otra. Si es así, la plantilla más especializada es la elegida por el proceso de pedido parcial.
Para producir la plantilla transformada, para cada tipo, no tipo o parámetro de plantilla de plantilla (incluidos los paquetes de parámetros de plantilla ([temp.variadic]) del mismo), sintetice un tipo único, valor o plantilla de clase respectivamente y sustitúyalos por cada aparición de Ese parámetro en el tipo de función de la plantilla.
Usando el tipo de función de la plantilla de función transformada, realice la deducción de tipo contra la otra plantilla como se describe en
[temp.deduct.partial]
.
Por esos párrafos, para cualquier función transformada de cualquier plantilla sintetizada Z0
y tipo T0
, que puede formar #1
, podemos hacer deducción de tipo con #2
. Pero las funciones transformadas del #2
con la plantilla ficticia Z2
con cualquier tipo T2
y cualquier conjunto de Args2
no vacío no se pueden deducir del #1
. #1
es obviamente más especializado que el #2
.
clang ++ tiene razón en este caso.
En realidad, este y el otro no se compilan (debido a que son ambiguos) en g ++ y clang. Parece que ambos compiladores tienen dificultades con los parámetros de plantilla de plantilla. (El último está claramente ordenado porque su orden es el mismo que el de la función no llamada).
Mientras intentaba implementar algunas cosas confiando en las plantillas variadas, tropecé con algo que no puedo explicar. He reducido el problema al siguiente fragmento de código:
template <typename ... Args>
struct A {};
template <template <typename...> class Z, typename T>
struct test;
template <template <typename...> class Z, typename T>
struct test<Z, Z<T>> {
static void foo() {
std::cout << "I''m more specialized than the variadic spec, hehe!" << std::endl;
}
};
template <template <typename...> class Z, typename T, typename ... Args>
struct test<Z, Z<T, Args...>> {
static void foo() {
std::cout << "I''m variadic!" << std::endl;
}
};
int main() {
test<A, A<int>>::foo();
}
Bajo gcc, produce un error porque considera que ambas especializaciones están igualmente especializadas cuando se intenta instanciar la test<A, A<int>>
:
main.cpp: In function ''int main()'':
main.cpp:25:24: error: ambiguous template instantiation for ''struct test<A, A<int> >''
test<A, A<int>>::foo();
^~
main.cpp:11:12: note: candidates are: template<template<class ...> class Z, class T> struct test<Z, Z<T> > [with Z = A; T = int]
struct test<Z, Z<T>> {
^~~~~~~~~~~~~
main.cpp:18:12: note: template<template<class ...> class Z, class T, class ... Args> struct test<Z, Z<T, Args ...> > [with Z = A; T = int; Args = {}]
struct test<Z, Z<T, Args...>> {
Sin embargo, Clang considera que la primera especialización es "más especializada" (a través de un pedido parcial: vea la siguiente sección), ya que compila bien e imprime:
Estoy más especializado que el variado spec, jeje!
Una demostración en vivo se puede encontrar en Coliru. También intenté usar la versión HEAD de gcc y obtuve los mismos errores.
Mi pregunta aquí es: ya que estos dos compiladores conocidos se comportan de manera diferente, ¿cuál es el correcto y este código es correcto en C ++?
Interpretación estándar ( C ++ 14 borrador actual )
De las secciones §14.5.5.1 y $ 14.5.5.2 del borrador estándar de C ++ 14, se activa el orden parcial para determinar qué especialización debe elegirse:
(1.2) - Si se encuentra más de una especialización coincidente, las reglas de orden parcial (14.5.5.2) se usan para determinar si una de las especializaciones es más especializada que las otras. Si ninguna de las especializaciones es más especializada que todas las demás especializaciones coincidentes, entonces el uso de la plantilla de clase es ambiguo y el programa está mal formado.
Ahora de acuerdo con §14.5.5.2, las especializaciones de plantillas de clase se transforman en plantillas de funciones a través de este procedimiento:
Para las especializaciones parciales de dos plantillas de clase, la primera es más especializada que la segunda si, dada la siguiente reescritura de dos plantillas de función, la primera plantilla de función es más especializada que la segunda según las reglas de ordenamiento de las plantillas de función (14.5.6.2):
(1.1) - la primera plantilla de función tiene los mismos parámetros de plantilla que la primera especialización parcial y tiene un único parámetro de función cuyo tipo es una especialización de plantilla de clase con los argumentos de plantilla de la primera especialización parcial, y
(1.2): la segunda plantilla de función tiene los mismos parámetros de plantilla que la segunda especialización parcial y tiene un único parámetro de función cuyo tipo es una especialización de plantilla de clase con los argumentos de plantilla de la segunda especialización parcial.
Por lo tanto, traté de reproducir el problema con las sobrecargas de plantillas de funciones que la transformación descrita anteriormente debería generar:
template <typename T>
void foo(T const&) {
std::cout << "Generic template/n";
}
template <template <typename ...> class Z, typename T>
void foo(Z<T> const&) {
std::cout << "Z<T>: most specialized overload for foo/n";
}
template <template <typename ...> class Z, typename T, typename ... Args>
void foo(Z<T, Args...> const&) {
std::cout << "Z<T, Args...>: variadic overload/n";
}
Ahora tratando de usarlo así:
template <typename ... Args>
struct A {};
int main() {
A<int> a;
foo(a);
}
produce un error de compilación [llamada ambigua] tanto en clang como en gcc: live demo . Esperaba que el Clang tuviera al menos un comportamiento consistente con el caso de plantilla de clase.
Entonces, esta es mi interpretación de la norma (que parece compartir con @Danh), por lo que en este momento necesitamos un language-lawyer para confirmarlo.
Nota: Busqué un poco el rastreador de errores de LLVM y no pude encontrar un ticket para el comportamiento observado en las sobrecargas de plantillas de función en esta pregunta.