Este é o meu código de exemplo:
#include <type_traits>
template <bool B>
struct S {
// #1
// template <bool C = B, class = std::enable_if_t<!C>>
// operator S<true>() const { return {}; }
// #2
// operator S<true>() const { return {}; }
};
int main() {
S<true> b1;
S<false> b2;
}
Ao compilar com g++ ( https://godbolt.org/z/P46qE8EGG ), não recebo nenhum aviso se habilito a opção 1, a opção 2 ou ambas. Eu esperaria um aviso de que é uma função de autoconversão quando eu habilitar o número 2, mas talvez o g++ não tenha esse aviso.
O verdadeiro problema é ao compilar com o clang. Recebo os seguintes avisos apenas se ativar o número 1:
erro: a função de conversão que converte 'S' para si mesmo nunca será usada [-Werror,-Wclass-conversion]
A ativação do nº 2 não aciona esse aviso e a desativação da instanciação de um deles b1
ou b2
não o impede. O que está acontecendo? Isso é um erro no clang?
EDIT: estou me referindo especificamente ao motivo pelo qual o número 1 produz o aviso. A outra informação pretendia mostrar minhas tentativas de tentar restringir o problema a um exemplo funcional menor
Correto, pode-se esperar avisos do compilador (melhores práticas não padrão), mas não há garantia de que um determinado compilador os forneça.
Clang está correto em não avisar
#2
, pois pode ser invocado, mostrado mais facilmente em um exemplo com definições excluídas:Por que? Como o construtor de cópia implícito copia
enquanto, no entanto,
S<false>
intoS<true>
não é uma operação de cópia no mesmo sentido.O construtor do modelo também está presente quando a classe "copiar de" é
S<false>
, então Clang pode parecer, à primeira vista, alertar incorretamente que esta função nunca será usada:No entanto, Clang provavelmente não é inteligente o suficiente para analisar o SFINAE, e podemos ver que a construção SFINAE é uma pista falsa para quando ou não o aviso é emitido, pois obtemos o mesmo diagnóstico no seguinte caso:
Percebemos que o Clang emite avisos para quando instanciamos
S<true>
, caso em que é realmente verdade: a função nunca será invocada.Neste ponto, podemos nos perguntar por que ele não emite um aviso para
#1
. Pode ser que uma função de modelo (#1
) esteja um degrau abaixo na resolução de sobrecarga, ou que a análise da função não-modelo não dependente (#2
) aconteça apenas uma vez, independente de incluir as instanciações das especializações de modelo de classe.Se tivermos certeza de que a função nunca será convertida para si mesma em nenhuma instanciação, os avisos, como esperado, desaparecerão:
Eu não ficaria surpreso se Clang rejeitasse um relatório de bug para este problema, já que o aviso é preciso para a especialização que ele sinaliza, a menos que algum esforço heróico seja feito para entender quando e quando uma determinada função de modelo não é SFINAE:d ou não.