c++ - installed - llvm 3.7 0
static_assert depende del parĂ¡metro de plantilla que no sea de tipo(comportamiento diferente en gcc y clang) (2)
template <int answer> struct Hitchhiker {
static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
Al intentar deshabilitar la
static_assert
instancias de plantilla general con
static_assert
, descubrí que el código anterior en
clang
genera el error de
static_assert
incluso cuando la plantilla no se instancia, mientras que
gcc
genera el error de afirmación solo cuando se instancia
Hitchhiker
con un parámetro distinto de
42
.
Jugueteando, encontré que esta afirmación:
template <int answer> struct Hitchhiker {
static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};
template <> struct Hitchhiker<42> {};
se comporta igual en ambos compiladores: la aserción solo se activa cuando se instancia la plantilla general.
¿Qué dice el estándar, qué compilador es el correcto?
g++ 4.9.2
clang++ 3.50
Ambos compiladores son correctos. Desde [temp.res] / 8:
Si no se puede generar una especialización válida para una plantilla, y esa plantilla no se instancia, la plantilla está mal formada, no se requiere diagnóstico.
No existe una especialización válida que pueda generarse a partir de la plantilla principal
Hitchhiker
, por lo que está mal formada, no se requiere diagnóstico.
clang elige emitir un diagnóstico de todos modos.
Si solo desea permitir
42
, simplemente no defina la plantilla general:
template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};
Citas encontradas por @TartainLlama
Si una instancia hipotética de una plantilla inmediatamente después de su definición estaría mal formada debido a una construcción que no depende de un parámetro de plantilla, el programa está mal formado; No se requiere diagnóstico.
N4296 [temp.res] / 8
Esto se aplica inmediatamente después de definir la plantilla primaria (la que tiene el
static_assert
).
Por lo tanto, la especialización posterior (para
42
) no se puede considerar, ya que aún no existe.
La siguiente pregunta es si
static_assert( sizeof(answer) != sizeof(answer),
depende
de la
answer
. Semánticamente no lo hace, sintácticamente sí lo hace, y de manera estándar:
Dentro de una plantilla, algunas construcciones tienen una semántica que puede diferir de una instancia a otra. Tal construcción depende de los parámetros de la plantilla.
N4296 [temp.dep] / 1
La construcción
sizeof(answer) != sizeof(answer)
no difiere de una instanciación a otra.
Por lo tanto, tal construcción
no depende
de los parámetros de la plantilla.
Lo que significa que el
static_assert
completo no depende del parámetro de plantilla.
Por lo tanto, su programa está mal formado, no se requiere diagnóstico.
Emitir un diagnóstico arbitrario (como el
static_assert
) es un comportamiento válido del compilador.
Falta el problema es el comportamiento válido del compilador.
El comportamiento de un programa compilado a partir de un programa mal formado, no requiere diagnóstico, no está definido por el estándar: es un comportamiento indefinido.
Los demonios nasales están permitidos.
Los intentos de fantasía (como
sizeof(int[answer])!=sizeof(int[answer])
pueden complacer al compilador de
Dios
actual, pero no hacen que su programa esté mejor formado.
Podría presentar un caso en el que es poco probable que el compilador pueda atraparlo, pero la forma incorrecta sigue siendo independiente de la capacidad del compilador de atraparlo con él. Como regla general, C ++ quiere dejarse a sí mismo (y a sus compiladores) la libertad de encontrar código de plantilla inválido "antes de la instanciación"; Esto significa que el código de la plantilla debe producir posiblemente un código legal.
Es posible que desee algo como
=delete
con un mensaje adjunto.