In c++14 die decltype(auto)
Redewendung eingeführt.
Typischerweise ist seine Verwendung zu ermöglichen auto
Erklärungen zur Verwendung der decltype
Regeln für den gegebenen Ausdruck.
Bei der Suche nach Beispielen für eine „gute“ Verwendung der Redewendung fallen mir nur Dinge wie die folgenden ein (by Scott Meyers), nämlich für die Rückgabetypableitung einer Funktion:
template<typename ContainerType, typename IndexType> // C++14
decltype(auto) grab(ContainerType&& container, IndexType&& index)
{
authenticateUser();
return std::forward<ContainerType>(container)[std::forward<IndexType>(index)];
}
Gibt es weitere Beispiele, bei denen diese neue Sprachfunktion nützlich ist?
Rückgabetypweiterleitung in generischem Code
Für nicht generischen Code, wie das erste Beispiel, das Sie gegeben haben, können Sie manuell auswählen, um eine Referenz als Rückgabetyp zu erhalten:
auto const& Example(int const& i)
{
return i;
}
aber in generischer Code du willst es können leiten Sie einen Rückgabetyp perfekt weiter ohne zu wissen, ob es sich um eine Referenz oder einen Wert handelt. decltype(auto)
gibt Ihnen diese Fähigkeit:
template<class Fun, class... Args>
decltype(auto) Example(Fun fun, Args&&... args)
{
return fun(std::forward<Args>(args)...);
}
Verzögerung des Rückgabetypabzugs in rekursiven Vorlagen
Im diese Fragen und Antworten Vor ein paar Tagen wurde eine unendliche Rekursion während der Template-Instanziierung festgestellt, als der Rückgabetyp des Templates als angegeben wurde decltype(iter(Int<i-1>{}))
anstatt decltype(auto)
.
template<int i>
struct Int {};
constexpr auto iter(Int<0>) -> Int<0>;
template<int i>
constexpr auto iter(Int<i>) -> decltype(auto)
{ return iter(Int<i-1>{}); }
int main() { decltype(iter(Int<10>{})) a; }
decltype(auto)
ist hier gewohnt Verzögern Sie den Rückgabetypabzug nachdem sich der Staub der Template-Instanziierung gelegt hat.
Andere Verwendungen
Sie können auch verwenden decltype(auto)
in anderen Zusammenhängen, z. B. dem Normentwurf N3936 auch Staaten
7.1.6.4 Autospezifizierer [dcl.spec.auto]
1 Die auto
und decltype(auto)
Typbezeichner bezeichnen einen Platzhaltertyp, der später ersetzt wird, entweder durch Ableitung von einem Initialisierer oder durch explizite Angabe mit einem nachgestellten Rückgabetyp. Die auto
type-specifier wird auch verwendet, um anzuzeigen, dass ein Lambda ein generisches Lambda ist.
2 Der Platzhaltertyp erscheinen kann mit einem Funktionsdeklarator in decl-specifier-seq, type-specifier-seq, conversion-function-id oder trailing-return-type, in jedem Kontext, in dem ein solcher Deklarator gültig ist. Wenn der Funktionsdeklarator einen Trailing-Return-Type (8.3.5) enthält, gibt dieser den deklarierten Rückgabetyp der Funktion an. Wenn der deklarierte Rückgabetyp der Funktion einen Platzhaltertyp enthält, wird der Rückgabetyp der Funktion gegebenenfalls aus Rückgabeanweisungen im Hauptteil der Funktion abgeleitet.
Der Entwurf enthält auch dieses Beispiel der Variableninitialisierung:
int i;
int&& f();
auto x3a = i; // decltype(x3a) is int
decltype(auto) x3d = i; // decltype(x3d) is int
auto x4a = (i); // decltype(x4a) is int
decltype(auto) x4d = (i); // decltype(x4d) is int&
auto x5a = f(); // decltype(x5a) is int
decltype(auto) x5d = f(); // decltype(x5d) is int&&
auto x6a = { 1, 2 }; // decltype(x6a) is std::initializer_list<int>
decltype(auto) x6d = { 1, 2 }; // error, { 1, 2 } is not an expression
auto *x7a = &i; // decltype(x7a) is int*
decltype(auto)*x7d = &i; // error, declared type is not plain decltype(auto)
Zitieren von Sachen aus Hier:
-
decltype(auto)
ist in erster Linie nützlich für Ableitung des Rückgabetyps von Weiterleitungsfunktionen und ähnlichen Wrappernwo Sie möchten, dass der Typ einen Ausdruck, den Sie aufrufen, genau „verfolgt“.
-
Zum Beispiel mit den folgenden Funktionen:
string lookup1();
string& lookup2();
- In C++11 könnten wir die folgenden Wrapper-Funktionen schreiben, die daran denken, die Referenz des Rückgabetyps beizubehalten:
string look_up_a_string_1() { return lookup1(); }
string& look_up_a_string_2() { return lookup2(); }
- In C++14 können wir das automatisieren:
decltype(auto) look_up_a_string_1() { return lookup1(); }
decltype(auto) look_up_a_string_2() { return lookup2(); }
-
Aber, decltype(auto)
ist nicht als weit verbreitetes Feature gedacht.
-
Insbesondere, obwohl es verwendet werden kann Deklarieren Sie lokale Variablenist dies wahrscheinlich nur ein Antimuster, da die Referenz einer lokalen Variablen nicht vom Initialisierungsausdruck abhängen sollte.
-
Außerdem ist es empfindlich, wie Sie die return-Anweisung schreiben.
-
Beispielsweise haben die beiden folgenden Funktionen unterschiedliche Rückgabetypen:
decltype(auto) look_up_a_string_1() { auto str = lookup1(); return str; }
decltype(auto) look_up_a_string_2() { auto str = lookup2(); return(str); }
- Der erste kehrt zurück
string
die zweite kehrt zurück string&
was ein Verweis auf die lokale Variable ist str
.
Von dem Vorschlag Sie können weitere Verwendungszwecke sehen.
Dieser Beitrag schlägt im Grunde vor, diese Redewendung zu vermeiden, da Sie Ihrem Compiler stackoverflow.com/a/20092875/2485710 weniger Optionen zur Optimierung geben, wenn Sie sie verwenden
– Benutzer2485710
8. Juni 2014 um 19:27 Uhr
Ich habe einmal verwendet
decltype(auto)
für etwas ähnlichestemplate<class U, V> decltype(auto) first(std::pair<U, V>& p) { return p.first; }
obwohl mir dann klar wurde, dass ich es verwenden musstereturn (p.first);
was überraschenderweise funktioniert (aber IIRC ist sogar beabsichtigt).– dyp
8. Juni 2014 um 19:50 Uhr
@ user2485710 Ich bin mir nicht sicher, ob es speziell um Optimierung geht, eher um das Potenzial für Unfälle, wenn
decltype(auto)
kann dazu führen, dass etwas wider Erwarten in das deklarierte Objekt kopiert/verschoben wird.– Unterstrich_d
17. April 2016 um 17:45 Uhr
In dem Beispiel, das Sie oben geben, wenn
container
ist eigentlich ein rvalue. Ich denke, die Verwendung von decltype(auto) kann zu versehentlichen Verweisen auf Dangles führen. Sie können jedoch den Werttyp von ContainerType zurückgeben, und das Kopieren von Ellision sollte Ihnen dasselbe wie decltype (auto) geben, aber sicher als Referenz verwendet werden godbolt.org/z/GsYjxs– Steve_Corrin
6. Oktober 2020 um 18:37 Uhr
Ja, hier ist ein weiteres Beispiel, bei dem der interne Wert des Containers zerstört wird, aber wir bitten um einen Verweis darauf von der Funktion godbolt.org/z/7jE5Me
– Steve_Corrin
7. Oktober 2020 um 18:26 Uhr