Especialización basada en validez de tamaño de matriz

8

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?

Amir Kirsh
fuente
template<int p> struct absolute<p, typename std::void_t<int[p]>>y template<int p> struct absoluteno es eso un duplicado?
Chipster
3
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"
LF
@LF, así que no estoy fallando aquí: godbolt.org/z/sfSxam es un error con clang y gcc?
Amir Kirsh
@AmirKirsh Olvidó deshabilitar las extensiones. godbolt.org/z/RB96uc
LF
Compila también en MSVC.
Eerorika

Respuestas:

4

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_tsimplemente 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-errorsGCC informa un error duro ya para

std::cout << absolute<5>() << std::endl;

en la especialización

template<int p>
struct absolute<p, typename std::void_t<int[-p]>>

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 -pcon p = 5convertir en std::size_tno es una expresión constante, por lo que el tipo de int[-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:

Si una sustitución da como resultado un tipo o expresión no válida, la deducción de tipo falla. Un tipo o expresión inválido es uno que estaría mal formado, con un diagnóstico requerido, si se escribe usando los argumentos sustituidos.

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 el

template<int p>
struct absolute<p, typename std::void_t<int[p]>>

especialización, donde int[p]evaluaría a int[-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-errorsen GCC y Clang.

nuez
fuente
Implementando nuestra propia versión de void_ttrabajos 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?
Amir Kirsh
@AmirKirsh Con su definición, también está eludiendo el problema 1980 de CWG. Ambos están relacionados con la forma en que se manejan las plantillas de alias que resuelven los tipos no dependientes. Su definición se resuelve en make_void<Ts...>::type, que es un tipo dependiente.
nogal
@AmirKirsh No me di cuenta de que GCC solo se queja de que el tamaño de una especialización no sea una expresión constante. He actualizado la respuesta para reflejar eso y parece aclararme aún más que el CCG es inconsistente.
nogal