2

Betrachten Sie die folgende Funktion:Get-Typ aus der Vorlagentyp weitergeleitet und nicht das Argument

template <class T> 
constexpr /* something */ f(T&& x) { 
    // do something 
} 

und lassen Sie uns sagen, dass ich sfinae basierend tun wollen von der Art eines weitergeleitet Argument an eine Funktion myfunction genannt geben. Eine Möglichkeit, dies zu erreichen ist:

template <class T> 
constexpr auto f(T&& x) -> decltype(myfunction(std::forward<T>(x))) { 
    // do something 
} 

statt, dies zu tun, ist es eine Möglichkeit, es in der Vorlage Ebene zu tun:

// This code won't compile 
template <class T, class R = decltype(myfunction(std::forward<T>(x)))> 
constexpr R f(T&& x) { 
    // do something 
} 

außer, dass ich keinen Zugang zu x noch so Dieser Code wird nicht kompiliert. Gibt es eine Möglichkeit, dies nur basierend auf T (möglicherweise mit std::declval) zu erreichen?

Hinweis: Dies ist kein X/Y-Problem, es ist nur ein Beispiel, um zu veranschaulichen, wo diese Situation passiert: Ich weiß nicht, wie man SFINAE mit Forwarding ohne Zugriff auf die Variable macht, denn für mich ist das Verhalten von std::forward immer noch ein wenig mysteriös.

Antwort

5

Ja, std::declval ist in der Tat der Schlüssel:

template <class T, class R = decltype(myfunction(std::declval<T>()))> 
constexpr R f(T&& x) { 
    // do something 
} 

Dies wird entweder SFINAE oder R wird der Rückgabetyp sein je nachdem, was Überlastung von myfunction gewählt wird.

Ihre Frage scheint mir, dass Sie eine Auffrischung benötigen, wie Referenz-Kollabieren funktioniert; Ich schlage vor, in diesem Bereich zu lesen (this scheint ein guter Ausgangspunkt zu sein).

+0

Ich würde erwähnen, dass dies ein schlechter Ansatz ist, denn man kann es leicht durch Übergeben eines zweiten Parameters brechen. – skypjack

+0

@skypjack: Richtig, so dass keine SFINAE stattfindet. – ildjarn

+0

Yeah, indem ich _break it_ sage, meinte ich das. Entschuldigung, wenn es nicht klar war. ;-) – skypjack

3

std::forward() hat nichts mit dieser Frage zu tun. Noch tut constexpr. Also lassen Sie uns mit dem Super Grund beginnen, eine Funktion, die nur ihr Argument von Wert zurückgibt:

template <class T> 
auto f(T x) -> decltype(x); 

Sicher, wir nur T verwenden könnte, aber das ist zu einfach. Nun ist die Frage, wie heben wir, dass in einem Template-Parameter, während immer noch SFINAE beibehalten (Zugegeben, es ist offensichtlich nicht möglich Substitution Scheitern hier, aber mit mir tragen):

template <class T, class R = decltype(x)> 
R f(T x); 

So geht das nicht, weil es keine x ist noch (oder schlimmer, es gibt einige nicht verwandte x woanders findet diese Namenssuche). Wir können die Argumentnamen in einem trailing-return-type verwenden, weil sie im scope sind, aber wir können sie nicht als Teil von expression-SFINAE in Standard-Template-Argumenten verwenden, weil sie noch nicht im scope sind.

Da dies jedoch Vorlagenparameter sind, interessieren wir uns nicht für ihre Werte. Wir kümmern uns nur um ihre Typen. Dies ist kein ausgewerteter Ausdruck. Also brauche ich nicht x, ich brauche etwas, das den gleichen Typ wie x hat. Ein erster gehen könnte sein:

template <class T, class R = decltype(T{})> 
R f(T x); 

Dies funktioniert ... solange T ist default-konstruierbar. Aber ich schreibe eine Vorlage, ich möchte keine Annahmen über Typen machen, die ich nicht machen muss.Anstatt also, kann ich so etwas wie:

template <class T> T make(); // never defined 

template <class T, class R = decltype(make<T>())> 
R f(T x); 

Und jetzt haben wir unseren willkürlichen Ausdruck vom Typ T, dass wir in decltype, in einem Standard-Template-Argumente verwenden können. Obwohl, wir sind immer noch ein wenig begrenzt mit make() - gibt es Typen, die Sie nicht durch einen Wert von einem functon (z. B. Arrays) zurückgeben können, so erweist es sich als nützlicher, Referenzen hinzuzufügen. Und wir brauchen eine weniger wahrscheinlich zu collide Namen als make:

template <class T> 
add_rvalue_reference<T> declval(); 

template <class T, class R = decltype(declval<T>())> 
R f(T x); 

Das ist genau der Punkt, der std::declval<T> - Sie geben einen Ausdruck vom Typ T in einem unevaluierten Kontext.


Zurück zu Ihrem ursprünglichen Problem. Wir verwenden den gleichen Gedankenprozess, wie man von decltype(x) zu decltype(declval<T>()) gelangt, aber wenden Sie es einfach auf einen anderen Ausdruck an. Anstelle von x haben wir myfunction(std::forward<T>(x)). Welches ist zu sagen, wir myfunction mit dem gleichen Typ wie unser Argument aufrufen:

template <class T, class R = decltype(myfunction(std::declval<T&&>()))> 
R f(T&& x); 

Aber aufgrund Kollabieren Regeln verweisen, std::declval<T&&> ist eigentlich die gleiche Funktion wie std::declval<T>, so können wir nur schreiben:

template <class T, class R = decltype(myfunction(std::declval<T>()))> 
R f(T&& x); 
Verwandte Themen