Function objects: Expertenhilfe erwünscht :)
-
Hallo,
bin gerade dabei einige for-Schlaufen mit for_each zu ersetzen. Dabei hatte ich jetzt schon ein paarmal Probleme der folgenden Art:
Angenommen ich habe einen std::vector aus ...sagen wir Widgets. Und ich habe eine Membermethode meiner Applikation:
class Widget; vector<Widget> widgetContainer; class Application; void Application::doSomething(const Widget& wg);Jetzt möchte ich jedes Element des Containers auf die Memberfunktion doSomething() loslassen. Mit for sieht das dann so aus:
Application myApp; ... for (vector<Widget>::const_iterator i = widgetContainer.begin(); i != widgetContainer.end(); ++i) myApp.doSomething(*i);Okay, jetzt versuche ich das ganze mit std::for_each:
Application myApp; ... for_each(widgetContainer.begin(), widgetContainer.end(), std::bind1st(std::mem_fun(&Application::doSomething), &myApp));Das geht leider nicht! Falls irgend jemand was damit anfangen kann... diese Compilermeldung bekomme ich ;):
c:\programme\c++\mingw\bin\..\lib\gcc\mingw32\4.4.0\include\c++\backward\binders.h|117|error: 'typename _Operation::result_type std::binder1st<_Operation>::operator()(typename _Operation::second_argument_type&) const [with _Operation = std::mem_fun1_t<void, MainDialog, const wxString&>]' cannot be overloaded|
Jetzt habe ich immerhin herausgefunden, dass es kompiliert, wenn ich doSomething per Value übergeben lasse:
void Application::doSomething(const Widget wg);
Nach dieser Rieseneinleitung jetzt meine eigentliche Frage: Wie schaffe ich es, dass das ganze kompiliert und ich meine Referenz behalten kann? (PS: Eine eigene Funktorklasse kommt für mich nicht in Frage, weil da IMHO die obige for-Schleife noch übersichtlicher ist.)
Viele Grüße, ZenJu
-
Schleife.
-
Schleife....das heißt?
-
Ich habe es eben ausprobiert und es gibt dasselbe Problem. Irgendwo in libstdc++ wird versucht, bei Benutzung von mem_fun eine Elementfunktion operator() zu erzeugen, bei der eine Referenz auf eine Referenz als Parameter erwartet wird. Das geht natürlich schief. Es sieht eigentlich wie ein libstdc++ Bug aus. Zumindest denke ich auch, dass es funktionieren sollte. Vielleicht können die MSVC++ Leute das auch mal ausprobieren und sagen, ob's bei ihnen klappt.
#include <iostream> #include <vector> #include <functional> #include <algorithm> #include <string> using namespace std; class A { public: void foo(string const& x); }; void A::foo(string const& x) { cout << x << '\n'; } int main() { vector<string> v; v.push_back("123"); v.push_back("hello"); v.push_back("world"); A a; for_each( v.begin(), v.end(), bind1st(mem_fun(&A::foo),&a) ); }Wenn man allerdings std::tr1::bind benutzt, klappt es
... #include <tr1/functional> ... using std::tr1::bind; namespace ph = std::tr1::placeholders; ... for_each( v.begin(), v.end(), bind(&A::foo,&a,ph::_1) ); ...Gruß,
SP
-
MSVC 2008 verweigert das auch:
c:\programme\microsoft visual studio 9.0\vc\include\functional(276) : error C2529: '_Right' : reference to reference is illegal
c:\programme\microsoft visual studio 9.0\vc\include\functional(281) : error C2529: '_Right' : reference to reference is illegal
c:\programme\microsoft visual studio 9.0\vc\include\functional(282) : error C2535: 'void std::binder1st<_Fn2>::operator ()(const std::basic_string<_Elem,_Traits,_Ax> &(&)) const' : member function already defined or declaredEs gibt wohl keine Lösung ohne boost oder TR1:
-
-
Hmmm.... Das müsste doch aber möglich sein. Man kann ja statt
result_type operator()(typename Operation::second_argument&);die Elementfunktion von binder1st auch einfach anders deklarieren:
result_type operator()(typename add_reference< typename Operation::second_argument>::type&);wobei add_reference so aussieht:
template<class T> struct add_reference{typedef T& type;}; template<class T> struct add_reference<T&>{typedef T& type;};Ich sehe jetzt zumindest nicht, warum es dann nicht funktionieren soll. Es kann natürlich sein, dass der Standard den ersten Fall vorschreibt. Dann wäre es meiner Meinung nach ein Defekt des Standards.
(Naja, mit std::bind (C++0x) wird sich das sowieso erledigt haben.)
Gruß,
SP
-
Sebastian Pizer schrieb:
Es kann natürlich sein, dass der Standard den ersten Fall vorschreibt.
Tut er.
Sebastian Pizer schrieb:
Dann wäre es meiner Meinung nach ein Defekt des Standards.
So ist es.
-
möglicherweise funktioniert es so:
#include <functional> namespace std { template <class S, class T, class A> class mem_fun1_t<S, T, A&> : public binary_function<T*, A, S> { S (T::*op)(A&); public: explicit mem_fun1_t(S (T::*p)(A&)) : op(p) {} S operator()(T* p, A& x) const { return (p->*op)(x); } }; }Das ist fast legal (eigentlich müsste ein UDT involviert sein).
Die Idee ist, statt das Referenz-auf-Referenz-Problem zu lösen, Referenzen als Argumente gar nicht erst (offiziell=second_argument) entstehen zu lassen.
Feedback?
-
Irgendwo in libstdc++ wird versucht, bei Benutzung von mem_fun eine Elementfunktion operator() zu erzeugen, bei der eine Referenz auf eine Referenz als Parameter erwartet wird. Das geht natürlich schief
Das habe ich auch gesehen. Aber ich verstehe nicht, dass mem_fun diesen Fall nicht unterstützt. In meinen Programmen wird jeder zweite Parameter (const Foo&) übergeben. Sowas MUSS doch den Leuten, die mem_fun programmiert haben aufgefallen sein...

-ZenJu
-
ZenJu schrieb:
Irgendwo in libstdc++ wird versucht, bei Benutzung von mem_fun eine Elementfunktion operator() zu erzeugen, bei der eine Referenz auf eine Referenz als Parameter erwartet wird. Das geht natürlich schief
Das habe ich auch gesehen. Aber ich verstehe nicht, dass mem_fun diesen Fall nicht unterstützt. In meinen Programmen wird jeder zweite Parameter (const Foo&) übergeben. Sowas MUSS doch den Leuten, die mem_fun programmiert haben aufgefallen sein...

-ZenJu
Dieser Teil des Standards wurde zu einem Zeitpunkt aufgenommen, als praktisch noch kein Compiler in der Lage, mit derartigem Code (Templates) richtig umzugehen. Ein Vielzahl von Fällen wurde also praktisch nur auf dem Papier entworfen und untersucht. Dabei können solche Probleme dann eben durchaus übersehen werden als nur wenige im Standardisierungskomitee Templates wirklich verstanden haben dürften. Und Funktionsadapter sind ja doch schon einer Abstraktionsebene höher als gewöhnliche Container aus T. Schließlich hat es auch bei boost mehrere Jahre und Anläufe gebraucht, bis sie bind richtig hinbekommen haben.