Am Samstag, 14. April 2012 18:32:07 UTC+2 schrieb Helmut Zeisel:
> Am Freitag, 13. April 2012 08:48:39 UTC+2 schrieb Helmut Zeisel:
> > On Apr 12, 11:18 pm, Daniel Krügler <
daniel.krueg...@googlemail.com>
> > wrote:
> > > Mit std::result_of geht es so:
> > >
> > > template<typename U, typename ... Args>
> > > typename std::result_of<U&(T&, Args&...)>::type
> > > forward(U u, Args ...args)
> > > {
> > > return ((*m).*u)(args...);
> > > }
> >
> > Danke, genau das habe ich gemeint.
>
> Ich hab noch ein wenig herumprobiert. Damit es auch klappt, wenn der Copy Constructor eines der Args geloescht ist, brauche ich
>
> template<typename U, typename ... Args>
> typename std::result_of<U&(T&, Args&...)>::type
> forward(U u, const Args& ...args)
> {
> return ((*m).*u)(args...);
> }
>
> (evtl auch noch "const U& u", falls U ein Funktionsobjekt sein kann).
So wie dein Aufruf definiert ist, kann U niemals ein Funktionsobjekt sein, da sich diese nicht dereferenzieren lassen. Aber wenn du "perfekte" Argumentenweiterleitung willst, schreib einfach
template<typename U, typename ... Args>
typename std::result_of<U&(T&, Args&&...)>::type
forward(U u, Args&&...args)
{
return ((*m).*u)(std::forward<Args>(args)...);
}
> Aber brauche ich wirklich
>
> std::result_of<U&(T&, Args&...)>::type; ?
>
> Die Version
>
> std::result_of<U(T, Args...)>::type;
>
> klappt auch - gibt es tatsaechlich Faelle, wo sich die beiden Versionen voneinander unterscheiden?
Es gibt ganz klar Fälle, wo das Ergebnis unterschiedlich wäre. Derzeit sind die meisten dieser Fälle allerdings noch nicht demonstrierbar, da aktuelle Compiler noch nicht alle C++11-Features (insbesondere ref-qualifizierte Element-Funktionen) unterstützen oder weil sie genau in deinem Beispiel nicht auftreten können. Rein technisch wird durch die genaue Form von result_of festgelegt, welcher Wert-Kategorie die Argumente bzw. das "aufrufbare Objekt" (callable object) angehört. Die Werte-Kategorie kann entscheidend sein, weil ja nicht alle Kombinationen gültig sind. In deinem konkreten Beispiel mit der speziellen Funktion f() spielt es keine Rolle, da die Funktion sowohl rvalue als auch lvalues akzeptiert. Dazu kommt, dass zufällig bei dir U nie eine Funktion sein kann, sondern nur ein Element-Zeiger.
In andere Fällen kann man das Problem leichter aufzeigen:
std::result_of<U(T, Args...)>::type
wäre z.B. ungültig, wenn U eine Funktion wäre, da Funktionen nicht als Rückgabetyp einer anderen Funktion verwendet werden können. Eine weitere Subtilität ist, dass durch die Enkodierung von result_of die "Constness" von allen Parametern verloren geht. In diesem Fall ist U& also zwingend. In obigen Beispiel würden T und Args zudem nie konstant sein können, da bei Werte-Funktionsparametern diese aus dem Funktionstyp entfernt werden. Zur Klarstellung:
double f(const double x, const double y)
hat die gleiche Signatur wie
double f(double x, double y)
Dies kann man leicht demonstrieren, wenn wir in deinem Beispiel anstelle von
Proxy<A> p(std::make_shared<A>());
ein
Proxy<const A> p(std::make_shared<A>());
deklarieren. Der result_of-Wert gibt immer noch etwas zurück, obwohl result_of hier nicht definiert sein dürfte, weil du versuchst, eine nicht-konstante Elementfunktion auf ein konstantes Objekt auszuführen. Wenn du danach das T in
std::result_of<U(T, Args...)>::type
zum korrekten T& änderst (Es ist deshalb korrekt, weil T als lvalue aufgerufen wird), meckert auch result_of sofort.
Ursprünglich wurde bei reference_wrapper genau so ein Fehler mit nicht-korrekten Parameter/Rückgabetypen gemacht, siehe:
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2017
Ebenso an anderen Stellen im Standard, siehe
http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-defects.html#2021
Insbesondere das erste Beispiel zeigt, was schieflaufen kann. Deine Definition wird auf jeden Fall scheitern, wenn dein Compiler ref-qualifizierte Funktionen unterstützt, weil dann entscheidend ist, ob die Funktion mit einem rvalue oder einem lvalue aufgerufen wird. Deine obige Definition tut so, als wäre es ein rvalue, was aber nicht mit deinem Code übereinstimmt.
Ich hoffe, die Sache ist etwas klarer geworden.
Besten Gruss aus Bremen,
Daniel Krügler