C++Talk.NET Forum Index C++Talk.NET
C++ language newsgroups
 
Archives   FAQFAQ   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   RegisterRegister 
 ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in 

Array
Goto page Previous  1, 2, 3 ... 13, 14, 15
 
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German)
View previous topic :: View next topic  
Author Message
Helmut Zeisel
Guest





PostPosted: Sat Apr 14, 2012 4:32 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote



Am Freitag, 13. April 2012 08:48:39 UTC+2 schrieb Helmut Zeisel:
Quote:
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).

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?

Helmut
Back to top
Daniel Krügler
Guest





PostPosted: Sun Apr 15, 2012 2:46 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote



Am Samstag, 14. April 2012 18:32:07 UTC+2 schrieb Helmut Zeisel:
Quote:
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)...);
}

Quote:
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
Back to top
Helmut Zeisel
Guest





PostPosted: Mon Apr 16, 2012 5:19 am    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote



Am Sonntag, 15. April 2012 16:46:05 UTC+2 schrieb Daniel Krügler:

Quote:
Ich hoffe, die Sache ist etwas klarer geworden.

Eigentlich bin ich jetzt noch mehr verwirrt ...

Die am einfachsten erklaerbare Loesung waere jetzt wohl,
statt des "schönen" std::result_of das "haessliche" (naja, das ist Gewohnheitsfrage) auto/decltype zu nehmen (sobald der Compiler mitspielt):

template<typename U, typename ... Args>
auto forward(U u, Args&&...args)
-> decltype( ((*(this->m)).*u)(std::forward<Args>(args)...) )
{
return ((*m).*u)(std::forward<Args>(args)...);
}

Da brauche ich dann nur vom return-Ausdruck abzuschreiben.

Helmut
Back to top
Helmut Zeisel
Guest





PostPosted: Mon Apr 16, 2012 12:22 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

On Thursday, April 12, 2012 11:18:28 PM UTC+2, Daniel Krügler wrote:

Quote:
aber gcc akzeptiert den Bezug auf "this->m" nicht. Dies lässt sich aber
beheben durch Verwendung von std::declval aus <utility>, womit wir uns einen entsprechenden Wert "erzeugen":

template<typename U, typename ... Args
auto forward(U u, Args ...args) -
decltype( ((*(std::declval<std::shared_ptr<T>&>())).*u)(args...) )
{
return ((*m).*u)(args...);
}

auto forward(U u, Args ...args) ->
decltype( (std::declval<T>().*u)(args...) )

bzw. besser

auto forward(U u, Args&& ...args) ->
decltype( (std::declval<T>().*u)(std::forward<Args>(args)...) )

klappt auch - gibt's da einen subtilen Unterschied zwischen
*(std::declval<std::shared_ptr<T>&>()) und std::declval<T>() ?

Helmut
Back to top
Daniel Krügler
Guest





PostPosted: Mon Apr 16, 2012 5:00 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Montag, 16. April 2012 07:19:59 UTC+2 schrieb Helmut Zeisel:
Quote:
Am Sonntag, 15. April 2012 16:46:05 UTC+2 schrieb Daniel Krügler:

Ich hoffe, die Sache ist etwas klarer geworden.

Eigentlich bin ich jetzt noch mehr verwirrt ...

Die am einfachsten erklaerbare Loesung waere jetzt wohl,
statt des "schönen" std::result_of das "haessliche" (naja, das ist Gewohnheitsfrage) auto/decltype zu nehmen (sobald der Compiler mitspielt):


Eigentlich nicht schwierig: Man setzt für jeden lvalue den Typ per lvalue-Referenz und für jeden rvalue per rvalue-Referenz. Ist doch einfach, oder?

Besten Gruss aus Bremen,

Daniel Krügler
Back to top
Daniel Krügler
Guest





PostPosted: Mon Apr 16, 2012 5:07 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Montag, 16. April 2012 14:22:39 UTC+2 schrieb Helmut Zeisel:
Quote:
On Thursday, April 12, 2012 11:18:28 PM UTC+2, Daniel Krügler wrote:

aber gcc akzeptiert den Bezug auf "this->m" nicht. Dies lässt sich aber
beheben durch Verwendung von std::declval aus <utility>, womit wir uns einen entsprechenden Wert "erzeugen":

template<typename U, typename ... Args
auto forward(U u, Args ...args) -
decltype( ((*(std::declval<std::shared_ptr<T>&>())).*u)(args...) )
{
return ((*m).*u)(args...);
}

auto forward(U u, Args ...args) -
decltype( (std::declval<T>().*u)(args...) )

Korrekt wäre

auto forward(U u, Args ...args) ->
decltype( (std::declval<T&>().*u)(args...) )

da T als lvalue eingeht, nicht als rvalue.

Quote:
bzw. besser

auto forward(U u, Args&& ...args) -
decltype( (std::declval<T>().*u)(std::forward<Args>(args)...) )

Auch hier gilt:

auto forward(U u, Args&& ...args) ->
decltype( (std::declval<T&>().*u)(std::forward<Args>(args)...) )

Quote:
klappt auch - gibt's da einen subtilen Unterschied zwischen
*(std::declval<std::shared_ptr<T>&>()) und std::declval<T>() ?

Wenn du *(std::declval<std::shared_ptr<T>&>()) mit std::declval<T&>() hätte ich gesagt "Nein, kein Unterschied": In beiden Fällen liefert der Ausdruck ja einen lvalue vom Typ T. Der Unterschied ist, dass std::declval<T>() einen rvalue liefert. Auch hier gilt die gleiche einfache Regel wie bei result_of: Verwende eine lvalue-Referenz, wenn du einen lvalue meinst, anderenfalls entweder den Typ oder dessen rvalue-Referenz.

Besten Gruss aus Bremen,

Daniel Krügler
Back to top
Helmut Zeisel
Guest





PostPosted: Mon Apr 16, 2012 6:05 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Montag, 16. April 2012 19:07:01 UTC+2 schrieb Daniel Krügler:
Quote:
Am Montag, 16. April 2012 14:22:39 UTC+2 schrieb Helmut Zeisel:

auto forward(U u, Args ...args) -
decltype( (std::declval<T>().*u)(args...) )

Korrekt wäre

auto forward(U u, Args ...args) -
decltype( (std::declval<T&>().*u)(args...) )

da T als lvalue eingeht, nicht als rvalue.

Ok, wenn man das so betrachtet, ist es logisch.

Gibt es aber wirklich ein Beispiel, bei dem sich

decltype( (std::declval<T>().*u)(args...) )

von

decltype( (std::declval<T&>().*u)(args...) )

unterscheidet?

Helmut
Back to top
Daniel Krügler
Guest





PostPosted: Tue Apr 17, 2012 5:36 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Montag, 16. April 2012 20:05:31 UTC+2 schrieb Helmut Zeisel:
Quote:
Gibt es aber wirklich ein Beispiel, bei dem sich

decltype( (std::declval<T>().*u)(args...) )

von

decltype( (std::declval<T&>().*u)(args...) )

unterscheidet?

Sicher, habe ich auch schon vorher beschrieben. Hier ein Beispiel:

struct R {
void f() &&;
};

struct L {
void f() &;
};

Die Funktion R::f() ist *nur* durch einen rvalue von R aufrufbar, d.h. wenn u den Wert &R::f annimmt, ist

decltype( (std::declval<T>().*u)(args...) )

wohlgeformt, aber

decltype( (std::declval<T&>().*u)(args...) )

wird vom Compiler abgelehnt.

Andersherum ist die Funktion L::f() *nur* durch einen lvalue von L aufrufbar, de.h. wenn u den Wert &L::f annimmt, ist

decltype( (std::declval<T&>().*u)(args...) )

wohlgeformt, aber

decltype( (std::declval<T>().*u)(args...) )

wird vom Compiler abgelehnt.

Es liegt lediglich an der obskuren Regel in "altem" C++, dass eine Elementfunktion sowohl von einem rvalue *als* auch von einem lvalue von "this" aufgerufen werden kann. C++11 stellt die obengenannten ref-qualifizierten Element-Funktionen zur Verfügung. Sie sind aber in gcc derzeit noch nicht verfügbar.

Besten Gruss aus Bremen,

Daniel Krügler
Back to top
Helmut Zeisel
Guest





PostPosted: Tue Apr 17, 2012 6:25 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Dienstag, 17. April 2012 19:36:10 UTC+2 schrieb Daniel Krügler:

Quote:
Es liegt lediglich an der obskuren Regel in "altem" C++, dass eine
Elementfunktion sowohl von einem rvalue *als* auch von einem lvalue von "this"
aufgerufen werden kann. C++11 stellt die obengenannten ref-qualifizierten
Element-Funktionen zur Verfügung.

Naja, momentan halte ich die noch die neue Reglen fuer obskurer. Aber langsam lichtet sich der Nebel.

Helmut
Back to top
Daniel Krügler
Guest





PostPosted: Wed Apr 18, 2012 6:43 pm    Post subject: Re: Wie verwende ich result_of fuer Member Function Pointer? Reply with quote

Am Dienstag, 17. April 2012 20:25:45 UTC+2 schrieb Helmut Zeisel:
Quote:
Am Dienstag, 17. April 2012 19:36:10 UTC+2 schrieb Daniel Krügler:

Es liegt lediglich an der obskuren Regel in "altem" C++, dass eine
Elementfunktion sowohl von einem rvalue *als* auch von einem lvalue von "this"
aufgerufen werden kann. C++11 stellt die obengenannten ref-qualifizierten
Element-Funktionen zur Verfügung.

Naja, momentan halte ich die noch die neue Reglen fuer obskurer. Aber langsam lichtet sich der Nebel.


Die alte Regel *ist* obskur, aber leider unfixbar, weil zuviel Code existiert, der ihn verwendet. Das fällt insbesondere auf, wenn man den sog. "impliziten Objektparameter" einer Elementfunktion vergleicht mit anderen Referenz-Parametern (Laut Sprache verhält er sich genau wie ein Referenzparameter vom Typ des dazugehörenden Klassentyps). Beaschte, dass folgender Code gültig ist:

struct S {
void foo();
};

int main() {
S().foo(); // Erzeuge rvalue von S und rufe foo() auf
}

Wenn man sich das genauer anschaut, verhält sich dieser Aufruf analog zu einem "umgeschriebenen" Aufruf einer freien Funktion, bei der der erste Parameter eine Referenz auf ein nicht-konstantes Objekt von S ist, also so:

void foo(S&);

Versuche nunmal, diese Funktion mit einem rvalue von S aufzurufen:

int main() {
foo(S());
}

Das ist *unzulässig*, da dies ein rvalue an eine lvalue-Referenz binden würde. Leider gibt es noch ein paar wenige Compiler, die diesen Code akzeptieren. Falls deiner dazu gehört: Das ist kein gültiges C++.

Durch die Einführung von ref-qualifizierten Elementfunktionen hat man dem Entwickler ein Mittel an die Hand gegeben, an geeigneten Stellen diese Inkonsistenz aufzulösen. Deklariere ich eine Element-Funktion mit & ref-Qualifizierer, d.h. so:

struct S {
void bar() &;
};

dann ist der Objekt-Parameter ein echter lvalue-Referenz-Parameter. Sie verhält sich also (In bezug auf den Objektparameter) exakt wie eine freie Funktion mit dieser Deklaration:

void bar(S&);

Das wiederum bedeutet, das aus diesem Grund die obengezeigte Konstruktion

int main() {
S().bar(); // Fehler!
}

nicht kompiliert, aus dem gleiche Grund, warum der Ausdruck

bar(S())

nicht kompiliert. Analog ist das bei einer Deklaration mit einem && ref-Qualifizierer:

struct S {
void bar() &&;
};

Man braucht wenig geistige Vorstellungskraft zu verstehen, dass sich diese Funktion verhält wie das folgende freie-Funktions-Analogon:

void bar(S&&);

Damit ist, klar, dass es sich auf jeden Fall über einen rvalue aufrufen lässt, also ist das hier gültig:

int main() {
S().bar(); // OK!
}

Und aus dem gleichen Grund ist es unzulässig, diese Funktion mit einem lvalue von S aufzurufen:

int main() {
S s;
s.bar(); // Fehler!
}

Man kann also vollkommen analog zu freien Funktionen verstehen, welche Argumente zu welchen Funktionssignaturen passen.

Ich hoffe, das beseitigt das geistige Chaos etwas.

Besten Gruss aus Bremen,

Daniel Krügler
Back to top
James Kanze
Guest





PostPosted: Sat Jan 12, 2013 11:38 am    Post subject: Re: new X->foo() Reply with quote

On Monday, December 31, 2012 12:10:03 PM UTC, Helge Kruse wrote:
Quote:
Am 31.12.2012 11:14, schrieb Stefan Reuther:

return (new X(42))->foo();

b) der 3er gcc erzeugt für diese Konstruktion deutlich mehr Code als für
X* x = new X(42);
return x->foo();

Beide Code-Varianten erzeugen eine Speicherleck.

Das weiß man nicht. Es kommt darauf an, was X tut. Wenn
X::X(int) registriert das Objekt in einen allgemein zugänglich
map, z.B., woher es spät gesucht wird. (Solche Idiome sind
häufig in Server, wo Objekte sowieso über mehreren Sessionen
leben, und werden gelöscht erst dann, wenn einen entsprechenden
Auftrag kommt.)

--
James
Back to top
Helge Kruse
Guest





PostPosted: Thu Jan 17, 2013 7:31 pm    Post subject: Re: new X->foo() Reply with quote

Am 12.01.2013 12:38, schrieb James Kanze:
Quote:
Beide Code-Varianten erzeugen eine Speicherleck.

Das weiß man nicht. Es kommt darauf an, was X tut. Wenn
X::X(int) registriert das Objekt in einen allgemein zugänglich
map, z.B., woher es spät gesucht wird.

Ja, natürlich könnte auch das new so überladen sein, dass etwas ganz
anderes passiert. Natürlich könnte es auch einen Garbage Collector
geben, der die Hausaufgaben für uns macht.

Aus dem gezeigten Code sind aber solche Annahmen nicht abzuleiten.

Helge
Back to top
Ernst Murnleitner
Guest





PostPosted: Thu Jan 17, 2013 9:41 pm    Post subject: Re: Umwandlung von negativem float nach unsigned int Reply with quote

SG schrieb:

Quote:
float f = -100;
unsigned short int i = int(f);


Dass i == -100 gelten sollte, steht aber im Widerspruch zu dem, was
Stefan R. aus dem C++ ISO Standard zitierte.

Die Regel von Stefan R. gilt aber nur für die Umwandlung von float in
UNSIGNED, nicht jedoch für die Umwandlung von float in SIGNED.
Da ich jetzt in obigem Beispiel den float zuerst in einen int umwandle
kommt die andere Regel zum tragen, die besagt, dass signed int in
unsigned int umgewandelt werden kann ohne bits zu verlieren.

Ich denke, das hat Markus S. in der parallelen Antwort in mathematischer
Form ausgedrückt.

Ernst
Back to top
Stefan Reuther
Guest





PostPosted: Fri Jan 18, 2013 4:28 pm    Post subject: Re: new X->foo() Reply with quote

Helge Kruse wrote:
Quote:
Am 12.01.2013 12:38, schrieb James Kanze:
Beide Code-Varianten erzeugen eine Speicherleck.

Das weiß man nicht. Es kommt darauf an, was X tut. Wenn
X::X(int) registriert das Objekt in einen allgemein zugänglich
map, z.B., woher es spät gesucht wird.

Ja, natürlich könnte auch das new so überladen sein, dass etwas ganz
anderes passiert. Natürlich könnte es auch einen Garbage Collector
geben, der die Hausaufgaben für uns macht.

Aus dem gezeigten Code sind aber solche Annahmen nicht abzuleiten.

Aus dem gezeigten Code ist aber genausowenig abzuleiten, dass da ein
Speicherleck entsteht.

Für die Neugier: hier ist die reale Funktion "foo" aus der Klasse "X",
die eigentlich "IntObjectContext" heißt:
IntContext*
IntObjectContext::initFirst()
{
index = type.findNextIndexNoWrap(0, false);
if (index != 0) {
// Found
return this;
} else {
// Not found
delete this;
return 0;
}
}
(index und type sind Member)


Stefan
Back to top
Helge Kruse
Guest





PostPosted: Sat Jan 19, 2013 1:40 pm    Post subject: Re: new X->foo() Reply with quote

Am 18.01.2013 18:28, schrieb Stefan Reuther:
Quote:

Für die Neugier: hier ist die reale Funktion "foo" aus der Klasse "X",
die eigentlich "IntObjectContext" heißt:
IntContext*
IntObjectContext::initFirst()
{
index = type.findNextIndexNoWrap(0, false);
if (index != 0) {
// Found
return this;
} else {
// Not found
delete this;
return 0;
}
}
(index und type sind Member)

Aha, die Method foo a.k.a initFirst liefert also einen this Zeiger. Dann
hat der Aufrufer noch eine Chance, das Leck zu vermeiden.

Dass IntObjectContext von IntContext abgeleitet ist sollte der Compiler
schon geprüft haben, damit "return this" funktioniert.

Helge
Back to top
Post new topic   Reply to topic    C++Talk.NET Forum Index -> C++ (German) All times are GMT
Goto page Previous  1, 2, 3 ... 13, 14, 15
Page 15 of 15

 
 


Powered by phpBB © 2001, 2006 phpBB Group