Modelos de sobrecarga ambíguos

16

Eu tenho o seguinte código de modelo

#include <vector>
#include <array>
#include <iostream>

template<typename T1>
void foo(std::vector<T1> bar) {
    std::cout << "GENERIC" << std::endl;
}

template<typename T1>
void foo(std::vector<std::vector<T1>> bar) {
    std::cout << "SPECIFIC (vector)" << std::endl;
}

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::vector<int>> a(2, std::vector<int> { 1, 2, 3});
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(a);
    foo(b);
}

que produz

SPECIFIC (vector)
GENERIC

Eu estou querendo saber por que a versão de vetor de vetor é chamada com o modelo específico, mas a versão de vetor de matriz é chamada com o genérico?

Xaser
fonte
2
FYI: Você pode simplificar isso, com o mesmo problema, removendo o exterior vectorde todos eles. Veja aqui
ChrisMM
@ChrisMM good catch. Este exemplo foi sintetizado no meu código de produção, onde a estrutura aninhada é necessária.
Xaser
5
MSVC chama versão de vetor de matrizes: godbolt.org/z/7Gfeb0
R2RT

Respostas:

8
template<typename T1, size_t SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

Você deve usar em std::size_tvez de int. corra aqui

Edit: Na verdade, seus comentários e minha intuição sobre o código me levaram a aprofundar o assunto. À primeira vista, um desenvolvedor padrão (como eu) espera que o compilador seja convertido intem std::size_t(porque eles são do tipo integral e a conversão implícita é muito trivial) e selecione a void foo(std::vector<std::array<T1, SIZE>> bar)melhor especialização. Então, ao ler a página de dedução de argumento de modelo , encontrei o seguinte:

Se um parâmetro de modelo que não seja do tipo for usado na lista de parâmetros e o argumento do modelo correspondente for deduzido, o tipo do argumento do modelo deduzido (conforme especificado em sua lista de parâmetros de modelo anexa, o que significa que as referências são preservadas) deve corresponder ao tipo do parâmetro exatamente o parâmetro de modelo não-tipo, exceto que os qualificadores cv são eliminados e exceto onde o argumento do modelo é deduzido de um limite de matriz - nesse caso, qualquer tipo integral é permitido, mesmo que seja sempre booleano:

Como sempre, é claro, você deve ler mais algumas vezes para entender o que significa :)

Então, um resultado interessante sai.

Nossa especialização desejada já não está selecionada, mas se o compilador tivesse sido forçado a selecionar, isso seria um erro.

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo(b); // P = std::vector<std::array<int,(int)SIZE>
            // A = std::vector<std::array<int,(unsigned_long)SIZE>>
            // error: deduced non-type template argument does not have the same
            // type as its corresponding template argument */
}

código de execução

Outra coisa interessante é:

Se o argumento de modelo não-tipo não tivesse sido deduzido, não haveria restrição que force os tipos de argumento e modelo a serem os mesmos.

#include <vector>
#include <array>
#include <iostream>

template<typename T1, int SIZE>
void foo(std::vector<std::array<T1, SIZE>> bar) {
    std::cout << "SPECIFIC (array)" << std::endl;
}

int main() {
    std::vector<std::array<int, 3>> b(2, std::array<int, 3> {4, 5, 6});

    foo<int,3>(b);
}

código de execução

arnes
fonte
@Xaser porque o segundo argumento do modelo de matriz é do tipo size_t...
Jean-Baptiste Yunès
2
Considerando o comentário do R2RT, parece haver diferenças específicas do compilador.
Xaser
8

Eu acho que isso é simplesmente devido a uma linha de[temp.deduct.call]/4

Em geral, o processo de dedução tenta encontrar valores de argumento de modelo que tornarão o A deduzido idêntico a A

Para esclarecer, Asignifica o parâmetro, de[temp.deduct.call]/1

... dedução do argumento do modelo com o tipo do argumento correspondente da chamada (chame de A) ...

Como já foi apontado, alterar template<typename T1, int SIZE>para template<typename T1, size_t SIZE>corrigir o problema que você está vendo. Como afirmado em [temp.deduct.call]/4, o compilador está procurando deduzir um Aque é idêntico a A. Como um std::arrayargumento tem argumentos de modelo <class T, size_t N>(de [array.syn]), seu segundo parâmetro é de fato size_t, não int.

Portanto, para a dedução de modelo, sua função genérica template<typename T1>é capaz de corresponder exatamente ao tipo de A, onde - como seu especialista template<typename T1, int SIZE>não é uma correspondência exata . Acredito que a MSVC esteja incorreta em sua dedução.

ChrisMM
fonte