2015-08-25 17 views
8

Warum kann der Compiler den Template-Parameter für std::forward nicht ableiten?Understanding std :: forward

ich meine:

#include <memory> 
#include <iostream> 

struct X{}; 

struct A{ 
    A(const X&) { std::cout << "cpy ctor\n"; } 
    A(X&&) { std::cout << "move ctor\n"; } 
}; 

X foo() { return {}; } 

template<typename T,typename Arg> 
T* factory(Arg&& a) 
{ 
    return new T(std::forward(a)); 
    // ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter 
} 

int main() 
{ 
    factory<A>(foo()); 
} 

Ich weiß, dass dies eine Design-Wahl (aufgrund der std::remove_reference in der Definition von std::forward) ist der Benutzer zu vermeiden, vergessen Sie den Typen angeben. Was ich nicht bekommen kann, ist: warum die Art und Weise, wie es implementiert wird, um eine Deduktion zu verhindern? Warum der Compiler nicht nur den Vorlagenparameter forward als Arg ableitet.

+4

Können Sie die Frage klären? Fragen Sie, warum das Design gewählt wurde, um Argumentabzüge zu vermeiden, oder warum die Art und Weise, wie es implementiert wurde, einen Abzug verhindert? – Angew

+1

Der Punkt, an dem der Typ manuell angegeben wird, ist so, dass "vorwärts" entscheiden kann, ob er sich "bewegen" soll oder nicht. Template Argument Abzug lässt Sie den Typ von "a" herausfinden, aber nicht, ob es bewegt werden soll oder nicht. – nwp

+1

@Angew Der zweite. –

Antwort

12

std::forward ist wie so erklärt:

template< class T > 
T&& forward(typename std::remove_reference<T>::type& t); 

typename std::remove_reference<T>::type ein Zusammenhang nicht abgeleitet ist. Der Compiler hat keine Möglichkeit zu wissen, welcher T abgeleitet werden sollte, da er die semantische Verbindung zwischen dem type Membertyp und einem gegebenen T nicht versteht. Es müsste alle Typen durchsuchen, um eine Übereinstimmung zu finden und Kollisionen irgendwie zu disambiguieren. Dies ist unangemessen, so dass es der Standard nicht erlaubt.

+0

Oh, ok :) ----- – Quentin

+0

Ja, das ist, was ich gefragt habe. Ich denke, das Lesen [http://stackoverflow.com/questions/25245453/what-is-anondeduced-context) wird den Rest erledigen. –

6

Der Grund, warum Sie für forward einen Typ angeben, von Design, ist das, was innerhalb der Funktion zu a passiert:

template<typename T,typename Arg> 
T* factory(Arg&& a) 
{ 
    // 'a' is always an lvalue here 

Da a ist immer ein L-Wert, gibt es nicht genügend Informationen in a selbst bestimmen können, ob es als Lvalue oder Rvalue übergeben wurde. Diese Informationen sind nur über den Typ Arg, der entweder X oder X& sein wird, verfügbar. Ohne diese zusätzliche Typinformationen, ist es unmöglich, ob es jetzt zu wissen a muss als L-Wert oder R-Wert weitergeleitet ... weshalb Sie es zur Verfügung stellen müssen:

return new T(std::forward<Arg>(a)); 
} 
+4

Nun, ich dachte OP stellte eine etwas andere Frage. Werde das hier trotzdem lassen. – Barry

+0

Ja, mein Schlechter. Es war nicht sehr klar von der Frage. Ich hoffe, dass der Schnitt hilft herauszufinden, was mein Problem war. –

0

Von C++ 11-Standard:

14.8.2.5 ableiten Vorlage Argumente von einem Typ

Die nicht abgeleitet Kontexte sind:

- Die verschachtelte-name-Angabe eines Typs, der angegeben wurde unter Verwendung einer qualifizierten Nummer

- Der Ausdruck eines Deklarationsspezifizierers.

- Ein nicht typisiertes Vorlagenargument oder eine Array-Grenze, in der einTeilausdruck auf einen Vorlagenparameter verweist.

- Ein Vorlagenparameter, der im Parametertyp einer Funktion verwendet wird Parameter mit einem Standardargument, das im Aufruf verwendet wird, für den die Argumentableitung ausgeführt wird.

etc ...

std::forward wird wie folgt erklärt:

template<typename _Tp> constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept

Nach ersten Satz oben: typename std::remove_reference<_Tp>::type nicht davon abgeleitete Kontext ist.