Tentando se especializar com base na validade do tamanho da 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:
O código acima funciona bem com o gcc, mas falha ao compilar com o clang .
Clang gera o erro: redefinição da estrutura do modelo 'absoluta'
Quem está certo?
Edição 2:
Tanto com o gcc como com o clang (se removermos a especialização negativa para trazer o clang de volta ao jogo), não está claro por que absolute<0>()
seleciona o modelo base. Não há nada errado com int[0]
, bem como com std::void_t<int[0]>
o que parece ser mais 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?
}
E ... se o modelo base for declarado apenas sem implementação, como:
// base template
template<int p, typename T = void>
struct absolute;
Ambos gcc e clang deixaria de compilação , queixando-se sobre uso inválido do tipo incompleta para a chamada: absolute<0>()
. Mesmo que pareça se encaixar no caso especializado.
Por que é que?
fonte
template<int p> struct absolute<p, typename std::void_t<int[p]>>
etemplate<int p> struct absolute
isso não é uma duplicata?int[0]
é proibido pelo padrão ISO C ++ timsong-cpp.github.io/cppwp/n4659/dcl.array#1 "seu valor deve ser maior que zero"Respostas:
Em relação ao erro de redefinição de Clang, consulte esta pergunta .
Originalmente, os IDs de modelo de alias, como os que
std::void_t
seriam simplesmente substituídos por seu tipo de alias, sem verificar os argumentos de falha na substituição. Isso foi alterado com a edição 1558 do CWG . Isso mudou apenas o padrão para exigir a falha de substituição nos argumentos do modelo, mas não esclarece se dois modelos que seriam equivalentes após a substituição do alias devem ser considerados equivalentes. Clang considera-os equivalentes, mas o GCC não. Esta é a edição aberta do CWG de 1980 .Com
-pedantic-errors
GCC relata um erro dura já hána especialização
porque supostamente o tamanho da matriz não é uma expressão constante. O tamanho de uma matriz deve ser uma expressão constante convertido de tipo
std::size_t
. Uma expressão constante convertida pode usar apenas conversões sem restrição. Portanto, é verdade que-p
comp = 5
convertido parastd::size_t
não é uma expressão constante, tornando o tipoint[-p]
mal formado, mas acho que isso deve causar uma falha de substituição, não um erro grave. [temp.deduct / 8] do padrão C ++ 17 (rascunho N4659) diz:E isso se aplica aqui. Os exemplos não normativos fornecidos após a citação incluem até tamanhos de matriz negativos como exemplo de falha de substituição.
É particularmente estranho que o
absolute<-5>()
GCC não relate o erro equivalente noespecialização, onde
int[p]
seria avaliado oint[-5]
qual também não possui um tamanho de expressão constante convertido.absolute<0>()
escolhe o modelo primário, porque é necessário que os tamanhos de matriz sejam maiores que zero, tornando inviáveis as especializações parciais. Matrizes de tamanho zero são uma extensão de idioma que pode ser desabilitada-pedantic-errors
no GCC e no Clang.fonte
void_t
trabalhos para clang e gcc: godbolt.org/z/-2C8mJ, para que pareça estar relacionado à edição 1558 do CWG ... ou também lida com a edição 1980 do CWG?make_void<Ts...>::type
, que é um tipo dependente.