2015-06-11 4 views
5

Bedenken Sie:Wie kann ich einen Dekltyp-Ausdruck mit einer Funktion schreiben, die eine nicht-konstante Referenz erwartet?

int convert_it(std::string& x) 
{ 
    return 5; 
} 

void takes_int_ref(int& i) 
{ 
} 

ich eine Funktion schreiben wollen, die besteht nur, wenn convert_it angewendet werden kann und das Ergebnis in takes_int_ref geben. Das heißt, die Funktion Körper ist:

template <typename A> 
void doit(A& a) 
{ 
    int i = convert_it(a); 
    takes_int_ref(i); 
} 

Allerdings, wenn ich tun:

template <typename A> 
auto doit(A& a) -> decltype(takes_int_ref(convert_it(a)), void()) 

es nicht, weil invalid initialization of non-const reference of type 'int&' from an rvalue of type 'int' funktioniert.

Ich dachte an die folgende Lösung, die funktioniert:

template <typename T> 
T& gimme_ref(T t) { throw std::runtime_error("No"); return t; } 

template <typename A> 
auto doit(A& a) -> decltype(takes_int_ref(gimme_ref(convert_it(a))), void()) 

Allerdings scheint es hackish und die decltype entspricht nicht mehr, was die Funktion Körper. Im Wesentlichen scheint das Problem zu sein, dass decltype nur einen Ausdruck benötigt, während hier zwei Anweisungen im Funktionskörper benötigt werden.

Was wäre der richtige Ansatz hier zu nehmen?

+0

sind 'std :: result_of' und' std :: enable_if' hilfreich? –

Antwort

9

Verwendung std::declval:

template <typename A> 
auto doit(A& a) -> decltype(
    takes_int_ref(std::declval< 
     decltype(convert_it(std::declval<A&>())) 
     &>()), void()) 
{ .. } 

std::declval<A&>() gibt Ihnen einen expresion vom Typ A&. convert_it(A&) wird entweder gültig sein oder nicht - wenn es ungültig ist, scheitern Sie dort. Wenn es gültig ist, sagen Sie es hat T. Dann versuchen Sie takes_int_ref mit einem T& zu rufen, um zu sehen, ob das gültig ist. Wenn ja, gelangen Sie zur void. Wenn nicht, Substitutionsfehler.

+0

Das klingt nach genau was ich brauche! Frage, warum brauchst du die '&' s in den Deklinationsausdrücken? – Claudiu

+1

@Claudiu Weil Sie eine Lvalue-Referenz möchten. 'declval ()' gibt Ihnen ein 'T &&', aber 'declval ()' gibt Ihnen ein 'T &'. – Barry

+0

Interessanterweise denke ich, dass meine Version nicht so hackisch ist, wenn ich 'gimme_ref' nur in' ref_of' umbenenne und den Körper überhaupt nicht definiere, sieht das sauberer aus: 'declltype (takes_int_ref (ref_of (convert_it (a))), void()) ' – Claudiu

1

Für die Aufzeichnung nach der std::declval Lösung zu sehen, meiner Wahrnehmung, was sich geändert hackish ist, und ich landete mit diesem gehen:

template <typename T> T& lref_of(T&&); 

template <typename A> 
auto doit(A& a) -> decltype(takes_int_ref(lref_of(convert_it(a))), void()) 
{ .. } 

Da ich aus einem Ausdruck starten bin und nicht eine Art, es ist sauberer zu tun lref_of(convert_it(a)) als std::declval<decltype(convert_it(a))&>(). Außerdem gibt lref_of keinen Fehler bei der Kompilierung, wenn er in einem Code verwendet wird, der besser ist, als wenn er einfach eine Ausnahme zur Laufzeit ausgibt.

Verwandte Themen