Was tut "-> decltype()" hinter einer Funktion?
-
Hallo,
hier ein kurzer Ausschnitt aus STL's vortrag "Don't help the Compiler":
template <> struct plus<void> { template <class T, class U> auto operator()(T&& t, U&& u) const -> decltype(forward<T>(t) + forward<U>(u)) { return forward<T>(t) + forward<U>(u); } }
Ich verstehe (halbwegs) was auto und decltype machen, auch deren unterschied, allerdings verstehe ich das obere Beispiel nicht, wo ist da der unterschied zu
decltype(forward<T>(t) + forward<U>(u)) operator()(T&& t, U&& u) const;
? Was genau tut das "-> decltype()" dahinter, ich nehme an, es hat was mit dem returntype zutun?
-
Huch, sowas haben sie uns in unserem C-Kurs in den 80ern nicht beigebracht.
Wäre cool wenn das jemand erklären könnte Also komplett und so.
-
tkausl schrieb:
Ich verstehe (halbwegs) was auto und decltype machen, auch deren unterschied, allerdings verstehe ich das obere Beispiel nicht, wo ist da der unterschied zu
decltype(forward<T>(t) + forward<U>(u)) operator()(T&& t, U&& u) const;
Der Unterschied ist, dass dein Vorschlag nicht funktioniert, da u und t an der Stelle noch nicht deklariert wurden.
? Was genau tut das "-> decltype()" dahinter, ich nehme an, es hat was mit dem returntype zutun?
Korrekt. Dieses Zusammenspiel von auto und "->" ist eine Möglichkeit, den Rückgabetyp erst hinter der Deklaration der Funktionsparameter anzugeben. Eben aus genau dem hier beschriebenen Grund, weil man sonst keine Rückgabewerte angeben könnte, die von den Parametern abhängen.
-
Ah, das hab ich mir fast gedacht, aber sollte ein decltype(auto) als Rückgabewert dann nicht das gleiche Ergebnis bringen? Ich meine, Type Deduction ist ja nicht blöd, der Compiler sieht ja die Expression die zurückgegeben wird.
-
tkausl schrieb:
Ich meine, Type Deduction ist ja nicht blöd, der Compiler sieht ja die Expression die zurückgegeben wird.
Das wurde aus Zeitgründen gestrichen
. Kein Scherz. Aber es ist in C++14 gekommen. Wenn dein Compiler aktuell genug ist, kannst du das also tatsächlich machen. Die Syntax ist das, was man intuitiv erwarten würde:
auto foo(int i) { return i + 1; }
decltype(auto)
funktioniert aber auch. Wobei, wenn ich mich recht entsinne, die genauen Regeln jeweils leicht unterschiedlich sind, wie die Type-Deduction genau durchgeführt wird.PS: Hat irgendwer sonst noch das Gefühl, dass dieser Thread nicht so ganz ernst gemeint ist? Ich fühle mich ein bisschen so, als wolle jemand meine C++14-Kompetenz testen.
-
SeppJ schrieb:
Wobei, wenn ich mich recht entsinne, die genauen Regeln jeweils leicht unterschiedlich sind, wie die Type-Deduction genau durchgeführt wird.
auto wirft constness und referenceness weg, decltype tut das nicht, daran kann ich mich noch erinnern
SeppJ schrieb:
PS: Hat irgendwer sonst noch das Gefühl, dass dieser Thread nicht so ganz ernst gemeint ist? Ich fühle mich ein bisschen so, als wolle jemand meine C++14-Kompetenz testen.
Die Frage war ernst gemeint
-
auto wirft constness und referenceness weg, decltype tut das nicht, daran kann ich mich noch erinnern
Jupp. Irgendwas in dieser Richtung. Ich bin aber zu faul, die genauen Regeln nach zu schauen, traue mich aber gleichzeitig nicht, diese ohne Nachgucken anzugeben.
tkausl schrieb:
Die Frage war ernst gemeint
Es fühlt sich halt irgendwie so an, als würdest du die Antworten bereits kennen, weil deine Fragen derart perfekt zu gewissen Antworten überleiten. Kann natürlich auch bloß Zufall (oder exzellentes Sprachgefühl
) sein und ich bin zu paranoid
.
-
SeppJ schrieb:
Es fühlt sich halt irgendwie so an, als würdest du die Antworten bereits kennen, weil deine Fragen derart perfekt zu gewissen Antworten überleiten. Kann natürlich auch bloß Zufall (oder exzellentes Sprachgefühl
) sein und ich bin zu paranoid
.
Liegt vermutlich daran, dass ich sehr gerne Vorträge von Scott Meyers, Stephan T. Lavavej und anderen (von der CppCon und auch andere), usw. anschaue, da ich diese sehr Spannend finde, dadurch gewisse "kniffe" aus dem Sprachkern lerne, gleichzeitig aber noch nichtmal die Grundlagen "richtig" gelernt habe, dadurch tuen sich teilweise Verständnisfragen auf, wo ich mir manchmal bereits denken kann, was etwas bedeutet, aber es eben gerne genau wissen möchte.
-
SeppJ schrieb:
funktioniert aber auch. Wobei, wenn ich mich recht entsinne, die genauen Regeln jeweils leicht unterschiedlich sind, wie die Type-Deduction genau durchgeführt wird.
*weinender otze*
-
Grade in einem anderen Vortrag von declval gehört, welches seit C++11 existiert und wenn ich so drüber nachdenke, sollte
decltype(forward<T>(declval<T>()) + forward<U>(declval<U>())) operator()(T&& t, U&& u) const;
funktionieren, ohne die Funktionsargumente zu benötigen.
-
Ja, Meyers erwähnt in seinen Vorträgen gerne dass es - IIRC - mindestens 3 verschiedene Type-Deduction Regeln bei C++ gibt.
Ich glaube es war bei Template-Parametern,auto
unddecltype
.Oder gibt's noch mehr? k.A., auf jeden Fall Chaos.
SeppJ schrieb:
auto foo(int i) { return i + 1; }
Ist das eigentlich das selbe wie
auto foo(int i) -> auto { return i + 1; }
?
-
decltype(auto)
hat einen wichtigen Unterschied zuauto
selbst: Es berücksichtigt die Wertkategorie des Rückgabetyps.[dcl.spec.auto]/7 schrieb:
If the placeholder is the
decltype(auto)
type-specifier, the declared type of the variable or return type of the function shall be the placeholder alone. The type deduced for the variable or return type is determined as described in 7.1.6.2, as though the initializer had been the operand of thedecltype
.Die Regeln für
decltype
sagen jedoch aus, dass für Ausdrücke die keine Identifier sind, ggf. Referenzen deduziert werden - abhängig von Wertkategorie des Ausdrucks. I.e.- Für xvalues werden Rvalue-Referenzen deduziert
- ... und für lvalues Lvalue-Referenzen.
- Bei prvalues ist es einfach der Typ.Das ist nützlich wenn wir einen proxycall durchführen:
decltype(auto) f(auto&&... args) { return myFunctionObject(std::forward<decltype(args)>(args)...); }
Gibt
myFunctionObject
nun bspw. einenstd::string&
zurück, so gibtf
auch einenstd::string&
zurück. Hätten wir hingegenauto
benutzt, so gäbef
std::string
zurück, es sei denn wir hätten trailing-return-types benutzt - wofür wir aber zu Faul sind.PS:
Der trailing-return-type inauto foo(int i) -> auto { return i + 1; }
ist zwar gültig aber völlig überflüssig. Also ja: Ist dasselbe wie ohne.
Was genau tut das "-> decltype()" dahinter, ich nehme an, es hat was mit dem returntype zutun?
Es ist der return type, aber wie bereits angedeutet verwendet man darin Dinge wie Klassenmember oder Funktionsparameter. Der Standard selbst bringt hier ein Beispiel:
[dcl.fct] schrieb:
[ Note: Typedefs and trailing-return-types are sometimes
convenient when the return type of a function is complex. For example, the functionfpif
above could have been declaredtypedef int IFUNC(int); IFUNC* fpif(int);
or
auto fpif(int)->int(*)(int);
A trailing-return-type is most useful for a type that would be more complicated to specify before the declarator-id:
template <class T, class U> auto add(T t, U u) -> decltype(t + u);
rather than
template <class T, class U> decltype((*(T*)0) + (*(U*)0)) add(T t, U u);
— end note ]