Plantillas de sobrecarga ambiguas

16

Tengo el siguiente código con plantilla

#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 produce

SPECIFIC (vector)
GENERIC

Me pregunto por qué la versión de vector de vector se llama con la plantilla específica, pero la versión de vector de matriz se llama con el genérico.

Xaser
fuente
2
FYI: puede simplificar esto, con el mismo problema, eliminando el exterior vectorde todos ellos. Ver aquí
ChrisMM
@ChrisMM buena captura. Este ejemplo fue sintetizado a partir de mi código de producción, donde la estructura anidada es necesaria.
Xaser
55
MSVC llama a la versión del vector de matrices: godbolt.org/z/7Gfeb0
R2RT

Respuestas:

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

Deberías usar en std::size_tlugar de int. corre aqui

Editar: En realidad, sus comentarios y mi intuición sobre el código me llevaron a profundizar en el tema. A primera vista, un desarrollador estándar (como yo) esperan compilador para convertir inta std::size_t(porque ambos son de tipo integral e implícitamente la conversión es muy trivial) y seleccionar void foo(std::vector<std::array<T1, SIZE>> bar)la mejor especialización. Entonces, mientras lee la deducción de argumento de plantilla página de encontré esto:

Si se utiliza un parámetro de plantilla que no es de tipo en la lista de parámetros, y se deduce el argumento de plantilla correspondiente, el tipo de argumento de plantilla deducido (como se especifica en su lista de parámetros de plantilla adjunta, lo que significa que se conservan las referencias) debe coincidir con el tipo de parámetro de plantilla que no es de tipo exactamente, excepto que los calificadores cv se descartan, y excepto donde el argumento de plantilla se deduce de una matriz vinculada; en ese caso, se permite cualquier tipo integral, incluso bool, aunque siempre sería cierto:

Como siempre, por supuesto, debes leer algunas veces más de una vez para entender lo que significa :)

Entonces sale un resultado interesante.

Nuestra especialización deseada ya no está seleccionada, pero si el compilador se hubiera visto obligado a seleccionar, sería un error.

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 */
}

Ejecutar código

Otra cosa interesante es:

Si no se hubiera deducido el argumento de plantilla sin tipo, no habría restricción que obligue a que los tipos de argumento y plantilla sean iguales.

#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);
}

Ejecutar código

arnes
fuente
@Xaser porque el argumento de la segunda plantilla de matriz es de tipo size_t...
Jean-Baptiste Yunès
2
Teniendo en cuenta el comentario de R2RT, parece haber diferencias específicas del compilador.
Xaser
8

Creo que esto se debe simplemente a una línea de[temp.deduct.call]/4

En general, el proceso de deducción intenta encontrar valores de argumento de plantilla que harán que la A deducida sea idéntica a A

Para aclarar, Asignifica el parámetro, de[temp.deduct.call]/1

... deducción de argumentos de plantilla con el tipo del argumento correspondiente de la llamada (llámelo A) ...

Como ya se ha señalado, cambiar template<typename T1, int SIZE>para template<typename T1, size_t SIZE>solucionar el problema que está viendo. Como se indica en [temp.deduct.call]/4, el compilador está buscando deducir una Aque sea idéntica a A. Como std::arraytiene argumentos de plantilla <class T, size_t N>(desde [array.syn]), su segundo parámetro es, de hecho size_t, noint .

Por lo tanto, para la deducción de plantilla, su función genérica de template<typename T1>puede coincidir exactamente con el tipo de A, donde, como su especialista template<typename T1, int SIZE>no es una coincidencia exacta . Creo que MSVC es incorrecto en su deducción.

ChrisMM
fuente