Especialização baseada na validade do tamanho da matriz

8

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?

Amir Kirsh
fonte
template<int p> struct absolute<p, typename std::void_t<int[p]>>e template<int p> struct absoluteisso não é uma duplicata?
Chipster
3
int[0]é proibido pelo padrão ISO C ++ timsong-cpp.github.io/cppwp/n4659/dcl.array#1 "seu valor deve ser maior que zero"
LF
@LF então não falha aqui: godbolt.org/z/sfSxam é um bug do clang e do gcc?
Amir Kirsh
@AmirKirsh Você esqueceu de desativar as extensões. godbolt.org/z/RB96uc
LF
Compila também no MSVC.
eerorika 17/03

Respostas:

4

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_tseriam 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-errorsGCC relata um erro dura já há

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

na especialização

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

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 -pcom p = 5convertido para std::size_tnão é uma expressão constante, tornando o tipo int[-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:

Se uma substituição resultar em um tipo ou expressão inválida, a dedução do tipo falhará. Um tipo ou expressão inválida é aquela que seria mal formada, com um diagnóstico necessário, se escrita usando os argumentos substituídos.

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 no

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

especialização, onde int[p]seria avaliado o int[-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-errorsno GCC e no Clang.

noz
fonte
Implementando nossa própria versão dos void_ttrabalhos 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?
Amir Kirsh
@AmirKirsh Com sua definição, você também está contornando o problema do CWG de 1980. Ambos estão relacionados à maneira como são manipulados os modelos de alias que resolvem tipos não dependentes. Sua definição é resolvida para make_void<Ts...>::type, que é um tipo dependente.
walnut
@AmirKirsh Eu não sabia que o GCC apenas reclama que o tamanho das especializações não é uma expressão constante. Atualizei a resposta para refletir isso e parece tornar ainda mais claro para mim que o GCC é inconsistente.
noz