Niejednoznaczne szablony przeciążenia

16

Mam następujący kod szablonu

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

który produkuje

SPECIFIC (vector)
GENERIC

Zastanawiam się, dlaczego wersja wektor-wektor jest wywoływana z określonym szablonem, ale wersja wektor-tablica jest wywoływana z ogólną?

Xaser
źródło
2
FYI: Możesz to uprościć, z tym samym problemem, usuwając zewnętrzną vectorz nich wszystkich. Zobacz tutaj
ChrisMM
@ChrisMM dobry połów. Ten przykład został zsyntetyzowany z mojego kodu produkcyjnego, w którym konieczna jest zagnieżdżona struktura.
Xaser
5
MSVC wywołuje wersję wektora tablic: godbolt.org/z/7Gfeb0
R2RT

Odpowiedzi:

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

Powinieneś użyć std::size_tzamiast int. biegnij tutaj

Edycja: W rzeczywistości twoje komentarze i moja intuicja dotycząca kodu skłoniły mnie do zagłębienia się w temat. Na pierwszy rzut oka standardowy programista (jak ja) oczekuje, że kompilator przekonwertuje się intna std::size_t(ponieważ oba są typem integralnym, a konwersja domyślna jest bardzo trywialna) i wybierze void foo(std::vector<std::array<T1, SIZE>> bar)najlepszą specjalizację. Tak więc podczas czytania strony z dedukcją argumentów szablonu znalazłem to:

Jeśli na liście parametrów zostanie użyty niepoprawny parametr szablonu, a wywnioskowany zostanie odpowiedni argument szablonu, typ wywnioskowanego argumentu szablonu (określony w załączonej liście parametrów szablonu, co oznacza, że ​​odniesienia zostaną zachowane) musi być zgodny z typem parametru nietypowy parametr szablonu, z wyjątkiem tego, że kwalifikatory cv są usuwane, i z wyjątkiem przypadków, gdy argument szablonu jest wywnioskowany z tablicy - w tym przypadku dozwolony jest dowolny typ całkowy, nawet bool, chociaż zawsze stałoby się to prawdą:

Jak zawsze, oczywiście, musisz przeczytać kilka razy więcej niż raz, aby zrozumieć, co to znaczy :)

Tak więc pojawia się interesujący wynik.

Nasza pożądana specjalizacja nie jest już wybrana, ale gdyby kompilator został zmuszony do wyboru, byłby to błąd.

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

uruchom kod

Kolejną interesującą rzeczą jest:

Gdyby nie wywnioskowano argumentu innego niż typ szablonu, nie byłoby żadnych ograniczeń, które wymuszałyby takie same argumenty i typy szablonów.

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

uruchom kod

arnes
źródło
@Xaser, ponieważ drugi argument szablonu jest typu size_t...
Jean-Baptiste Yunès
2
Biorąc pod uwagę komentarz R2RT, wydają się występować różnice specyficzne dla kompilatora.
Xaser
8

Myślę, że wynika to po prostu z jednej linii z[temp.deduct.call]/4

Ogólnie rzecz biorąc, proces dedukcji próbuje znaleźć wartości argumentów szablonu, dzięki którym wydedukowane A będzie identyczne z A.

Wyjaśnienie Aoznacza parametr od[temp.deduct.call]/1

... odjęcie argumentu szablonu z typem odpowiedniego argumentu wywołania (nazwij go A) ...

Jak już wspomniano, zmiana w template<typename T1, int SIZE>celu template<typename T1, size_t SIZE>rozwiązania problemu, który widzisz. Jak stwierdzono w [temp.deduct.call]/4, kompilator chce wydedukować Aidentyczny z A. Ponieważ an std::arrayma argumenty szablonu <class T, size_t N>(od [array.syn]), jego drugim parametrem jest tak naprawdę size_tnie int.

Dlatego do odliczenia szablonu twoja ogólna funkcja template<typename T1>jest w stanie dopasować dokładnie typ A, gdzie-jak twoja specjalizacja template<typename T1, int SIZE>nie jest dokładnym dopasowaniem. Uważam, że MSVC nie ma racji w swoich wnioskach.

ChrisMM
źródło