C ++ 11 make_pair mit angegebenen Vorlagenparametern wird nicht kompiliert

83

Ich habe gerade mit g ++ 4.7 (einem der späteren Schnappschüsse) mit aktiviertem -std = c ++ 11 herumgespielt. Ich habe versucht, einen Teil meiner vorhandenen Codebasis zu kompilieren, und ein fehlgeschlagener Fall verwirrt mich etwas.

Ich würde mich freuen, wenn jemand erklären kann, was los ist.

Hier ist der Code:

#include <utility>
#include <iostream>
#include <vector>
#include <string>

int main ( )
{
    std::string s = "abc";

    // 1 ok
    std::pair < std::string, int > a = std::make_pair ( s, 7 );

    // 2 error on the next line
    std::pair < std::string, int > b = std::make_pair < std::string, int > ( s, 7 );

    // 3 ok
    std::pair < std::string, int > d = std::pair < std::string, int > ( s, 7 );

    return 0;
}

Ich verstehe , dass make_pair ist gemeint als (1) Fall verwendet werden (wenn ich die Typen angeben, dann könnte ich auch verwenden (3)), aber ich verstehe nicht, warum es in diesem Fall versagt.

Der genaue Fehler ist:

test.cpp: In function int main()’:
    test.cpp:11:83: error: no matching function for call to make_pair(std::string&, int)’
    test.cpp:11:83: note: candidate is:
    In file included from /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/utility:72:0,
                 from test.cpp:1:
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note: template<class _T1, class _T2> constexpr std::pair<typename std::__decay_and_strip<_T1>::__type, typename std::__decay_and_strip<_T2>::__type> std::make_pair(_T1&&, _T2&&)
    /gcc4.7/usr/local/lib/gcc/i686-pc-linux-gnu/4.7.0/../../../../include/c++/4.7.0/bits/stl_pair.h:274:5:
note:   template argument deduction/substitution failed:
    test.cpp:11:83: note:   cannot convert s (type std::string {aka std::basic_string<char>}’) to type std::basic_string<char>&&’

Auch hier lautet die Frage nur: "Was ist los?" Ich weiß, dass ich das Problem beheben kann, indem ich die Vorlagenspezifikation entferne, aber ich möchte nur wissen, was hier unter der Decke fehlschlägt.

  • g ++ 4.4 kompiliert diesen Code ohne Probleme.
  • Das Entfernen von -std = c ++ 11 wird auch problemlos mit Code kompiliert.
vmpstr
quelle
6
Eine ausgezeichnete Frage. Ein weiteres Beispiel für eine subtile Änderung in C ++ 11, ähnlich der Änderung in der std::vectorKonstruktion . Zumindest dieser ergibt einen Compilerfehler und keine stille Änderung der Semantik.
James McNellis
1
Wenn ich eine ganzzahlige Variable habe i. Ich möchte ein Paar mit i und einem anderen Objekt machen. Wie genau soll ich makepair anrufen? 1) make_pair <* i, obj> 2) int && j = i; make_pair <j, obj>? Beide funktionieren nicht. Was ist der richtige Weg, um es zu tun?
PHcoDer

Antworten:

133

So std::make_pairsoll es nicht verwendet werden; Sie sollten die Vorlagenargumente nicht explizit angeben.

C ++ 11 verwendet std::make_pairzwei Argumente vom Typ T&&und U&&, wobei Tund UVorlagentypparameter sind. Tatsächlich sieht es so aus (ohne Rückgabe):

template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);

Wenn Sie std::make_pairdie Argumente des Vorlagentyps aufrufen und explizit angeben, findet kein Argumentabzug statt. Stattdessen werden die Typargumente direkt in die Vorlagendeklaration eingesetzt, was Folgendes ergibt:

[return type] make_pair(std::string&& argT, int&& argU);

Beachten Sie, dass diese beiden Parametertypen rWertreferenzen sind. Sie können also nur an r-Werte binden. Dies ist kein Problem für das zweite Argument, das Sie übergeben 7, da dies ein r-Wert-Ausdruck ist. sEs handelt sich jedoch um einen l-Wert-Ausdruck (er ist kein temporärer Ausdruck und wird nicht verschoben). Dies bedeutet, dass die Funktionsvorlage nicht mit Ihren Argumenten übereinstimmt, weshalb Sie den Fehler erhalten.

Warum funktioniert es also, wenn Sie nicht explizit angeben, was Tund Uin der Vorlagenargumentliste enthalten sind? Kurz gesagt, r-Wert-Referenzparameter sind in Vorlagen speziell. Zum Teil aufgrund einer Sprachfunktion, die als Referenzkollabieren bezeichnet wird , kann ein rvalue-Referenzparameter vom Typ A&&, bei dem Aes sich um einen Vorlagentypparameter handelt, an jede Art von gebunden werden A.

Es spielt keine Rolle, ob Aes sich um einen l-Wert, einen r-Wert, einen const-qualifizierten, einen flüchtigen oder einen nicht qualifizierten Wert handelt, der A&&an dieses Objekt gebunden werden kann (wiederum genau dann, wenn Aes sich selbst um einen Vorlagenparameter handelt).

In Ihrem Beispiel rufen wir an:

make_pair(s, 7)

Hier sist ein Wert vom Typ std::stringund 7ein Wert vom Typ int. Da Sie die Vorlagenargumente für die Funktionsvorlage nicht angeben, wird der Vorlagenargumentabzug durchgeführt, um die Argumente herauszufinden.

Zu binden s, ein L - Wert, um T&&die Compiler folgern, Tzu sein std::string&, ein Argument des Typs ergibt std::string& &&. Es gibt jedoch keine Verweise auf Referenzen, so dass diese "Doppelreferenz" zusammenbricht, um zu werden std::string&. sist ein Match.

Es ist einfach zu binden 7an U&&: der Compiler kann ableiten Uzu sein int, einen Parameter des Typs ergibt int&&, die erfolgreich bindet , 7weil es ein R - Wert ist.

Es gibt viele Feinheiten bei diesen neuen Sprachfunktionen, aber wenn Sie eine einfache Regel befolgen, ist es ziemlich einfach:

Wenn ein Vorlagenargument aus den Funktionsargumenten abgeleitet werden kann, lassen Sie es ableiten. Geben Sie das Argument nicht explizit an, es sei denn, Sie müssen es unbedingt tun.

Lassen Sie den Compiler die harte Arbeit machen, und in 99,9% der Fälle wird es genau das sein, was Sie sowieso wollten. Wenn es nicht das ist, was Sie wollten, wird normalerweise ein Kompilierungsfehler angezeigt, der leicht zu identifizieren und zu beheben ist.

James McNellis
quelle
6
Dies ist eine sehr gute und umfassende Erklärung. Danke dir!
vmpstr
1
@ James - ist das "eine einfache Regel" aus einem anderen Artikel oder einer Antwort, die ich lesen sollte?
Michael Burr
4
@ MichaelBurr: Nein, das habe ich mir gerade ausgedacht. :-) Also ich hoffe es ist wahr! Ich denke es ist wahr ... diese Regel funktioniert fast immer für mich.
James McNellis
1
@ James: Danke. Die 'Zitatbox' um mich herum ließ mich denken, dass es etwas war, das ursprünglich woanders geschrieben wurde. Diese Antwort war wirklich informativ und ich wollte nur sicherstellen, dass mir nichts anderes fehlt.
Michael Burr
2
Gilt das auch für Tupel?
Ferruccio