c++ - template - Alias de plantillas variables como argumentos de plantillas
template en c++ pdf (1)
Primero un poco de código, luego un poco de contexto, luego la pregunta:
template <typename T> using id = T;
template <template <typename...> class F, typename... T>
using apply1 = F <T...>;
template <template <typename...> class F>
struct apply2
{
template <typename... T>
using map = F <T...>;
};
// ...
cout << apply1 <id, int>() << endl;
cout << apply2 <id>::map <int>() << endl;
Tanto el Clang 3.3 como el gcc 4.8.1 compilan esto sin error, aplicando la metafunción de identidad a int
, por lo que ambas expresiones se evalúan como int
(cero) por defecto.
El hecho de que id
es una template <typename>
mientras que apply1
, apply2
espere que una template <typename...>
me preocupara en primer lugar. Sin embargo, es bastante conveniente que este ejemplo funcione porque, de lo contrario, las metafunciones como apply1
, apply2
tendrían que ser mucho más complicadas.
Por otro lado, tales alias de plantillas causan serios problemas en el código del mundo real que no puedo reproducir aquí: errores frecuentes del compilador interno para gcc y un comportamiento inesperado menos frecuente para el clang (solo en pruebas SFINAE más avanzadas).
Después de meses de prueba y error, ahora instalo y pruebo el código en el (experimental) gcc 4.9.0, y aquí viene el error:
test.cpp: In instantiation of ‘struct apply2<id>’:
test.cpp:17:22: error: pack expansion argument for non-pack parameter ‘T’ of alias template ‘template<class T> using id = T’
using map = F <T...>;
^
Bien, parece que este código no era válido todo este tiempo, pero gcc se bloqueó de varias maneras en lugar de informar del error. Curiosamente, si bien apply1
, apply2
parecen ser equivalentes, el error solo se notifica para apply2
(que es mucho más útil en la práctica). En cuanto a clang, realmente no puedo decir.
En la práctica, parece que no tengo más remedio que seguir el gcc 4.9.0 y corregir el código, aunque se volverá mucho más complejo.
En teoría, me gustaría saber qué dice la norma: ¿es válido este código? Si no, ¿es inválido el uso de apply1
también? o solo apply2
?
EDITAR
Solo para aclarar que todos los problemas que he tenido hasta ahora se refieren a alias de plantillas, no a estructuras de plantillas. Por ejemplo, considere la siguiente modificación:
template <typename T> struct id1 { using type = T; };
// ...
cout << typename apply1 <id1, int>::type() << endl;
cout << typename apply2 <id1>::map <int>::type() << endl;
Esto compila la multa e imprime 0
en ambos casos, en la versión 3.3, gcc 4.8.1, gcc 4.9.0.
En la mayoría de los casos, mis soluciones han estado introduciendo una estructura de plantilla intermedia antes del alias. Sin embargo, ahora estoy tratando de usar metafunciones para parametrizar las pruebas genéricas de SFINAE y en este caso tengo que usar alias directamente, porque las estructuras no deben ser instanciadas. Solo para tener una idea, una parte del código real está here .
ISO C ++ 11 14.3.3 / 1:
Un argumento de plantilla para un parámetro de plantilla de plantilla será el nombre de una plantilla de clase o una plantilla de alias , expresada como expresión-id.
Además, no veo ninguna excepción especial para los parámetros de plantilla de plantilla variadic.
Por otro lado, tales alias de plantillas causan serios problemas en el código del mundo real que no puedo reproducir aquí: errores frecuentes del compilador interno para gcc y un comportamiento inesperado menos frecuente para el clang (solo en pruebas SFINAE más avanzadas).
Raíz de problemas puede estar en otros lugares. Debe intentar localizar el código que causa el error interno del compilador; simplemente elimine las partes no relacionadas una por una (o use algún tipo de búsqueda binaria, es decir, dividir y conquistar), y verifique si todavía hay errores en cada etapa.
En cuanto al error GCC 4.9.0, intente cambiar
template <typename... T>
using map = F <T...>;
a
template <typename... U>
using map = F <U...>;
Quizás esto ayude a entender lo que GCC ve.