Betrachten Sie den folgenden Code .
struct any
{
template <typename T>
operator T &&() const;
template <typename T>
operator T &() const;
};
int main()
{
int a = any{};
}
Hier wird der zweite Konvertierungsoperator durch die Überlastauflösung ausgewählt. Warum?
Soweit ich es verstehe, sind die beiden Betreiber abgeleitet operator int &&() const
und operator int &() const
jeweils. Beide sind in der Reihe der realisierbaren Funktionen. Das Lesen von [over.match.best] hat mir nicht geholfen herauszufinden, warum letzteres besser ist.
Warum ist die letztere Funktion besser als die erstere?
template <typename T> operator T &&() const &&; template <typename T> operator T &() const &;
bringt es dazu, den ersten anzurufen.Antworten:
Der zurückgegebene Konvertierungsoperator
T&
wird bevorzugt, da er spezialisierter ist als der zurückgegebene KonvertierungsoperatorT&&
.Siehe C ++ 17 [temp.deduct.partial] / (3.2):
und / 9:
quelle
Die abgeleiteten Rückgabewertumwandlungsoperatoren sind etwas seltsam. Die Kernidee ist jedoch, dass es sich wie ein Funktionsparameter verhält, um auszuwählen, welcher verwendet wird.
Und bei der Entscheidung zwischen
T&&
undT&
denT&
Gewinnen in den Überlastungsauflösungsregeln. Dies soll ermöglichen:arbeiten.
T&&
kann mit einem l-Wert übereinstimmen, aber wenn sowohl der l-Wert als auch die universelle Referenzüberladung verfügbar sind, wird der l-Wert bevorzugt.Der richtige Satz von Konvertierungsoperatoren ist wahrscheinlich:
oder auch
um zu verhindern, dass eine fehlgeschlagene Verlängerung der Lebensdauer Sie beißt.
[SNIP]
Was dann bei der Auswahl von Überladungen von "spezialisierteren" Regeln abhängt:
Somit
operator T&&
ist es nicht mindestens so spezialisiert wieoperator T&
, während kein Regelstaatoperator T&
nicht mindestens so spezialisiert ist wieoperator T&&
, alsooperator T&
ist es spezialisierter alsoperator T&&
.Speziellere Vorlagen gewinnen Überlastungsauflösung über weniger, alles andere ist gleich.
quelle
&&
vs&
behandelt wird, wenn Vorlagenüberladungen ausgewählt werden, aber das Herausarbeiten des genauen Textes würde eine Weile dauern .Wir versuchen ein
int
von einem zu initialisierenany
. Der Prozess dafür ist es:Finde heraus, wie wir das machen können. Bestimmen Sie also alle unsere Kandidaten. Diese stammen aus den nicht expliziten Konvertierungsfunktionen, in die
int
über eine Standardkonvertierungssequenz ( [over.match.conv] ) konvertiert werden kann . Der Abschnitt enthält diesen Satz:Wählen Sie den besten Kandidaten.
Nach Schritt 1 haben wir zwei Kandidaten.
operator int&() const
undoperator int&&() const
beide werden zumint
Zwecke der Auswahl von Kandidatenfunktionen als nachgiebig angesehen. Welches ist der beste Kandidat, der nachgibtint
?Wir haben einen Tiebreaker, der l-Wert-Referenzen gegenüber r-Wert-Referenzen bevorzugt ( [over.ics.rank] /3.2.3 ). Wir binden hier jedoch keine Referenz, und das Beispiel dort ist etwas invertiert - dies gilt für den Fall, dass der Parameter eine Referenz von lvalue vs rvalue ist.
Wenn dies nicht zutrifft, wenden wir uns an den Tiebreaker [over.match.best] /2.5, der die speziellere Funktionsvorlage bevorzugt.
Im Allgemeinen gilt als Faustregel, dass eine spezifischere Konvertierung die beste Übereinstimmung darstellt. Die lWert-Referenzkonvertierungsfunktion ist spezifischer als die Weiterleitungsreferenzkonvertierungsfunktion, daher wird sie bevorzugt. Es gibt nichts an dem, was
int
wir initialisieren, was einen r-Wert erfordert (hätten wir stattdessen einen initialisiertint&&
,operator T&() const
wäre das kein Kandidat gewesen).quelle
T&&
gewinnt, nicht wahr ? Ah, aber wir haben keinen Wert, an den wir gebunden sind. Oder wir? Bei der Auswahl unserer Kandidaten müssen einige Wörter definiert haben, wie wir gekommen sind,int&
undint&&
war das eine Bindung?