Mehrdeutige Überlastungsvorlagen

16

Ich habe den folgenden Vorlagencode

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

was produziert

SPECIFIC (vector)
GENERIC

Ich frage mich, warum die Vektor-von-Vektor-Version mit der spezifischen Vorlage aufgerufen wird, aber die Vektor-von-Array-Version mit der generischen?

Xaser
quelle
2
Zu Ihrer Information: Sie können dies mit demselben Problem vereinfachen, indem Sie vectorbei allen das Äußere entfernen . Siehe hier
ChrisMM
@ ChrisMM guter Fang. Dieses Beispiel wurde aus meinem Produktionscode synthetisiert, in dem die verschachtelte Struktur erforderlich ist.
Xaser
5
MSVC ruft die Vektor-von-Arrays-Version auf: godbolt.org/z/7Gfeb0
R2RT

Antworten:

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

Sie sollten std::size_tanstelle von verwenden int. lauf hier

Bearbeiten: Eigentlich haben mich Ihre Kommentare und meine Intuition bezüglich des Codes dazu gebracht, mich mit dem Thema zu befassen. Auf den ersten Blick ein Standard - Entwickler (wie ich) erwarten Compiler zu konvertieren intzu std::size_t(weil sie beide integralen Typ sind und implizit Umwandlung ist sehr trivial) und wählen Sie void foo(std::vector<std::array<T1, SIZE>> bar)als beste Spezialisierung. Beim Lesen der Seite zum Abzug von Vorlagenargumenten habe ich Folgendes gefunden:

Wenn in der Parameterliste ein Nicht-Typ-Vorlagenparameter verwendet wird und das entsprechende Vorlagenargument abgeleitet wird, muss der Typ des abgeleiteten Vorlagenarguments (wie in der beigefügten Vorlagenparameterliste angegeben, dh Referenzen bleiben erhalten) mit dem Typ des übereinstimmen Nicht typisierter Vorlagenparameter, außer dass cv-Qualifizierer gelöscht werden und außer wenn das Vorlagenargument von einem gebundenen Array abgeleitet wird - in diesem Fall ist jeder ganzzahlige Typ zulässig, auch bool, obwohl er immer wahr werden würde:

Wie immer müssen Sie natürlich mehrmals lesen, um zu verstehen, was es bedeutet :)

Es ergibt sich also ein interessantes Ergebnis.

Unsere gewünschte Spezialisierung ist bereits nicht ausgewählt, aber wenn der Compiler zur Auswahl gezwungen worden wäre, wäre dies ein Fehler.

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

Code ausführen

Eine andere interessante Sache ist:

Wenn das Nicht-Typ-Vorlagenargument nicht abgeleitet worden wäre, gäbe es keine Einschränkung, die die Gleichheit von Argument und Vorlagentyp erzwingt.

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

Code ausführen

Arnes
quelle
@ Xaser, weil das zweite Vorlagenargument des Arrays vom Typ ist size_t...
Jean-Baptiste Yunès
2
In Anbetracht des Kommentars von R2RT scheint es compilerspezifische Unterschiede zu geben.
Xaser
8

Ich denke das liegt einfach an einer Zeile von[temp.deduct.call]/4

Im Allgemeinen versucht der Abzugsprozess, Vorlagenargumentwerte zu finden, die das abgeleitete A mit A identisch machen

Zur Verdeutlichung Abedeutet der Parameter von[temp.deduct.call]/1

... Vorlagenargumentabzug mit dem Typ des entsprechenden Arguments des Aufrufs (nennen Sie es A) ...

Wie bereits erwähnt, wird template<typename T1, int SIZE>das template<typename T1, size_t SIZE>angezeigte Problem durch Ändern auf behoben. Wie in angegeben, [temp.deduct.call]/4versucht der Compiler, eine zu schließen A, die mit identisch ist A. Da ein std::arrayTemplate-Argument <class T, size_t N>(von [array.syn]) hat, ist sein zweiter Parameter tatsächlich size_tnicht int.

Daher wird für die Vorlage Abzug, Ihre generische Funktion template<typename T1>ist in der Lage zu passen genau die Art von A, wo-wie Ihre spezialisiert template<typename T1, int SIZE>ist keine genaue Übereinstimmung. Ich glaube, MSVC ist in seinem Abzug falsch.

ChrisMM
quelle