static_assert dependente do parâmetro de modelo não-tipo (comportamento diferente no gcc e clang)

template  struct Hitchhiker { static_assert(sizeof(answer) != sizeof(answer), "Invalid answer"); }; template  struct Hitchhiker {}; 

Ao tentar desativar a instanciação geral de modelo com static_assert , descobri que o código acima no clang gera o erro assert, mesmo quando o modelo não é instanciado, enquanto o gcc gera o erro assert apenas ao instanciar o Hitchhiker com um parâmetro diferente de 42 .

Ao brincar, descobri que isso afirma:

 template  struct Hitchhiker { static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer"); }; template  struct Hitchhiker {}; 

se comporta da mesma maneira em ambos os compiladores: a afirmação entra em ação somente quando o modelo geral é instanciado.

O que diz o padrão, qual compilador está correto?

 g++ 4.9.2 clang++ 3.50 

Cotações encontradas por @TartainLlama

Se uma instanciação hipotética de um modelo imediatamente após sua definição for mal formada devido a um constructo que não depende de um parâmetro de modelo, o programa é mal formado; nenhum diagnóstico é necessário.

N4296 [temp.res] / 8

Isso se aplica imediatamente após o modelo principal ser definido (aquele com static_assert ). Assim, a especialização posterior (para 42 ) não pode ser considerada, pois ainda não existe.

A próxima pergunta é se static_assert( sizeof(answer) != sizeof(answer), depende da answer . Semanticamente não, sintaticamente, e padrão:

Dentro de um modelo, algumas construções têm semântica que pode diferir de uma instanciação para outra. Tal construção depende dos parâmetros do modelo.

N4296 [temp.dep] / 1

O constructo sizeof(answer) != sizeof(answer) não difere de uma instanciação para outra. Portanto, tal construção não depende dos parâmetros do modelo. O que significa que o static_assert inteiro não depende do parâmetro do modelo.

Assim, o seu programa está mal formado, sem necessidade de diagnóstico. Emitir um diagnóstico arbitrário (como a falha static_assert ) é um comportamento válido do compilador. A falta do problema é um comportamento válido do compilador. O comportamento de um programa compilado a partir de um programa mal formado, sem necessidade de diagnóstico, não é definido pelo padrão: é um comportamento indefinido. Demônios nasais são permitidos.

Tentativas de fantasia (como sizeof(int[answer])!=sizeof(int[answer]) podem agradar o compilador god atual, mas não tornam seu programa mais bem formado.

Você poderia fazer um caso em que o compilador dificilmente conseguirá capturá-lo, mas o mal-formado permanece independente da capacidade do compilador de capturá-lo com ele. Como regra geral, o C ++ quer deixar a si mesmo (e seus compiladores) liberdade para encontrar código de modelo inválido “antes da instanciação”; Isso significa que o código do modelo deve produzir código possivelmente legal.

É possível que você queira algo como =delete com uma mensagem anexada.

Ambos os compiladores estão corretos. De [temp.res] / 8:

Se nenhuma especialização válida puder ser gerada para um modelo e esse modelo não for instanciado, o modelo será mal formado, nenhum diagnóstico será necessário.

Não existe uma especialização válida que possa ser gerada a partir do modelo principal Hitchhiker , por isso é mal formada, sem necessidade de diagnóstico. clang escolhe emitir um diagnóstico de qualquer maneira.

Se você quiser permitir apenas 42 , simplesmente não defina o modelo geral:

 template  struct Hitchhiker; template <> struct Hitchhiker<42> {};