Tratando de especializarse en función de la validez del tamaño de la matriz:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return 0; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
// negative case template
template<int p>
struct absolute<p, typename std::void_t<int[-p]>> {
operator int () const { return -p; }
};
int main() {
std::cout << absolute<5>() << std::endl;
std::cout << absolute<-5>() << std::endl;
std::cout << absolute<0>() << std::endl;
}
Problema 1:
El código anterior funciona bien con gcc pero no puede compilarse con clang .
Clang genera el error: redefinición de la estructura de la plantilla 'absoluta'
¿Quién tiene la razón?
Problema # 2:
Tanto con gcc como con clang (si eliminamos la especialización negativa para devolver el clang al juego), no está claro por qué absolute<0>()
selecciona la plantilla base. No hay nada de malo en int[0]
lo std::void_t<int[0]>
que parece ser más especializado:
// base template
template<int p, typename T = void>
struct absolute {
operator int () const { return -1; }
};
// positive case template
template<int p>
struct absolute<p, typename std::void_t<int[p]>> {
operator int () const { return p; }
};
int main() {
std::cout << absolute<5>() << std::endl; // 5
std::cout << absolute<0>() << std::endl; // -1, why not 0?
}
Y ... si la plantilla base se declara sin implementación, como:
// base template
template<int p, typename T = void>
struct absolute;
Tanto gcc y sonido metálico dejarían de compilación , se quejan en el uso inválido de tipo incompleto para la llamada: absolute<0>()
. A pesar de que parece encajar en el caso especializado.
¿Porqué es eso?
fuente
template<int p> struct absolute<p, typename std::void_t<int[p]>>
ytemplate<int p> struct absolute
no es eso un duplicado?int[0]
está prohibido por el estándar ISO C ++ timsong-cpp.github.io/cppwp/n4659/dcl.array#1 "su valor será mayor que cero"Respuestas:
Con respecto al error de redefinición de Clang, vea esta pregunta .
Originalmente, los identificadores de plantilla de las plantillas de alias, como
std::void_t
simplemente se reemplazarían por su tipo de alias sin verificar los argumentos para el error de sustitución. Esto cambió con el problema 1558 de CWG . Esto solo cambió el estándar para requerir un error de sustitución en los argumentos de la plantilla que se realizarán, pero no aclara si dos plantillas que serían equivalentes después de reemplazar el alias deberían considerarse equivalentes. Clang los considera equivalentes, pero GCC no. Este es el número abierto de CWG de 1980 .Con
-pedantic-errors
GCC informa un error duro ya paraen la especialización
porque supuestamente el tamaño de la matriz no es una expresión constante. El tamaño de una matriz debe ser una expresión de tipo constante convertida
std::size_t
. Una expresión constante convertida solo puede usar conversiones no limitantes. Por lo tanto, es cierto que-p
conp = 5
convertir enstd::size_t
no es una expresión constante, por lo que el tipo deint[-p]
enfermos formada, pero yo creo que debería provocar un fallo de sustitución, no es un error de hardware. [temp.deduct / 8] del estándar C ++ 17 (borrador N4659) dice:Y esto se aplica aquí. Los ejemplos no normativos dados al seguir la cita incluso incluyen tamaños de matriz negativos como ejemplo de falla de sustitución.
Es particularmente extraño que para
absolute<-5>()
GCC no se informa el error equivalente en elespecialización, donde
int[p]
evaluaría aint[-5]
cuál tampoco tiene un tamaño de expresión constante convertido.absolute<0>()
elige la plantilla primaria, porque los tamaños de matriz deben ser mayores que cero, lo que hace que las especializaciones parciales no sean viables. Las matrices de tamaño cero son una extensión de idioma que se puede deshabilitar-pedantic-errors
en GCC y Clang.fuente
void_t
trabajos para clang y gcc: godbolt.org/z/-2C8mJ, por lo que parece estar relacionado con el problema 1558 de CWG ... ¿o también maneja el problema 1980 de CWG?make_void<Ts...>::type
, que es un tipo dependiente.