Modèles de surcharge ambigus

16

J'ai le code modèle suivant

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

qui produit

SPECIFIC (vector)
GENERIC

Je me demande pourquoi la version vecteur de vecteur est appelée avec le modèle spécifique, mais la version vecteur de tableau est appelée avec le générique?

Xaser
la source
2
FYI: Vous pouvez simplifier cela, avec le même problème, en supprimant l'extérieur vectorsur chacun d'eux. Voir ici
ChrisMM
@ChrisMM bonne capture. Cet exemple a été synthétisé à partir de mon code de production, où la structure imbriquée est nécessaire.
Xaser
5
MSVC appelle la version vectorielle des tableaux: godbolt.org/z/7Gfeb0
R2RT

Réponses:

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

Vous devez utiliser à la std::size_tplace de int. courir ici

Edit: En fait, vos commentaires et mon intuition sur le code m'ont amené à creuser le sujet. À première vue, un développeur standard (comme moi) attendre compilateur pour convertir intà std::size_t(car ils sont à la fois type intégral et la conversion implicite est très trivial) et choisir void foo(std::vector<std::array<T1, SIZE>> bar)comme meilleure spécialisation. Donc, en lisant la page de déduction des arguments du modèle, j'ai trouvé ceci:

Si un paramètre de modèle non type est utilisé dans la liste des paramètres et que l'argument de modèle correspondant est déduit, le type de l'argument de modèle déduit (comme spécifié dans sa liste de paramètres de modèle englobante, ce qui signifie que les références sont préservées) doit correspondre au type de l'argument paramètre de type non-type exactement, sauf que les qualificatifs cv sont supprimés, et sauf lorsque l'argument de modèle est déduit d'une limite de tableau - dans ce cas, tout type intégral est autorisé, même bool bien qu'il devienne toujours vrai:

Comme toujours, bien sûr, vous devez lire plusieurs fois plus d'une fois pour comprendre ce que cela signifie :)

Un résultat intéressant en ressort donc.

Déjà notre spécialisation souhaitée n'est pas sélectionnée mais si le compilateur avait été forcé de sélectionner, ce serait une erreur.

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

exécuter du code

Une autre chose intéressante est:

Si l'argument de modèle non-type n'avait pas été déduit, il n'y aurait aucune restriction qui forcerait les types d'argument et de modèle à être identiques.

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

exécuter du code

arnes
la source
@Xaser car le second argument de template du tableau est de type size_t...
Jean-Baptiste Yunès
2
Compte tenu du commentaire de R2RT, il semble y avoir des différences spécifiques au compilateur.
Xaser
8

Je pense que cela est simplement dû à une ligne de[temp.deduct.call]/4

En général, le processus de déduction tente de trouver des valeurs d'argument de modèle qui rendront le A déduit identique à A

Pour clarifier, Asignifie le paramètre, de[temp.deduct.call]/1

... déduction d'argument modèle avec le type de l'argument correspondant de l'appel (appelez-le A) ...

Comme cela a déjà été souligné, le fait de changer template<typename T1, int SIZE>pour template<typename T1, size_t SIZE>résoudre le problème que vous voyez. Comme indiqué dans [temp.deduct.call]/4, le compilateur cherche à déduire un Aqui est identique à A. Comme un std::arraya des arguments de modèle <class T, size_t N>(de [array.syn]), son deuxième paramètre ne l'est size_tpas int.

Par conséquent, pour la déduction de modèle, votre fonction générique de template<typename T1>peut correspondre exactement au type de A, où votre spécialiste template<typename T1, int SIZE>n'est pas une correspondance exacte . Je crois que MSVC est incorrect dans sa déduction.

ChrisMM
la source