Template auf const oder & Typen?
-
Hallo,
ich habe mir heute den Nachmittag mit dem schreiben einer Collection Klasse vertrieben.
Gemeint ist mehr oder weniger eine Wrapper um eine std::list<T*> der es erlaubt mittels einer Methode apply() eine beliebige Methode für jedes Objekt in der Liste aufzurufen. Funktioniert soweit alles. Hier kurzer Codeausschnitttemplate <class T> class Coll { public: // apply fuer parameterlose Methoden template <class R> void apply( R (T::*method) ( ) ); // apply fuer Methoden mit einem Parameter template <class R,class A> void apply( R (T::*method) (A) , A a); // ... usw fuer mehrparametrige Methoden protected: typedef typename list<T*>::iterator iterator; typedef typename list<T*>::const_iterator const_iterator; std::list<T*> m_list; }; template <class T> template <class R> void Coll<T>::apply( R (T::*method) ( ) ) { iterator it; for ( it=m_list.begin(); it != m_list.end(); it++ ) { ((*it)->*method)(); } } template <class T> template <class R,class A> void Coll<T>::apply( R (T::*method) (A) , A a) { iterator it; for ( it=m_list.begin(); it != m_list.end(); it++ ) ((*it)->*method)(a); }Ich war bis vor kurzem noch total happy, dass alles funktioniert. Bis ich dann versucht habe Methoden mit const oder & Parametern aufzurufen.
z.B.foo(const Bar& b)Da war es vorbei mit der Freude

Gibts da irgendeine Möglichkeit ohne apply für sämtliche Permutationen von
(A),(A&),(const A), (const A&) etc. zu deklarieren und definieren?Vielen Dank schon mal
buergi
-
Diese Coll-Klasse bringt Dir irgendwie nicht viel. Du kannst auch direkt eine std::list<> nehmen und einfach for_each aufrufen. Damit bist Du auch viel flexibler.
Dass Du Zeiger speicherst, ignoriere ich jetzt mal.
#include <iostream> #include <list> #include <algorithm> #include <functional> using namespace std; class dings { public: explicit dings(int zahl) : z(zahl) {} void transmogrify() const {cout << z;} private: int z; }; int main() { list<dings> l; l.push_back(dings(1)); l.push_back(dings(2)); l.push_back(dings(3)); for_each(l.begin(), l.end(), mem_fun_ref(&dings::transmogrify)); cout << '\n'; }Gruß,
SP
-
Ich vergaß: mem_fun und mem_fun_ref unterstützen auch Elementfunktionen, die einen Parameter erwarten. Schreibst Du halt bind2dn(mem_fun_ref(&dings::blah),42) als Funktor.
BTW: Es gibt noch mehr Fälle, über die man sich Gedanken machen muss. ZB unterstütut Dein Code keine const-member functions.

Dein Problem mit const& bzgl Parameter habe ich übrigens nicht verstanden.
Gruß,
SP
-
Hallo Sebastian,
erst mal danke für die Antwort,
for_each und bind1st/bind2nd habe ich schon gefunden, das Problem ist das sie nicht erlauben mehr Parameter zu übergeben, deshalb die Idee mit den vielen apply Methoden für jeweils andere Parameterzahl (bis 7 oder so das reicht in jedem Fall).
Problem war nur, dass ich mittemplate <class R,class A> void apply( R (T::*method) (A) , A a);zwar alle Methoden abdecke die irgendeinen einzigen Parameter verlangen, der allerdings weder const noch & etc sein darf. Ich kann damit also z.B. nicht aufrufen
Obj::foo(const string& str) { cout << str << endl; } // [...] string s("foobar"); objlist.apply(&Obj::foo,s);Ist zwar ein sinnloses Beispiel aber hoff man versteht was gemeint ist.
Aber ich habe die Lösung mit den apply Methoden schon aufgegeben, da mach ich lieber iterator loops über die Liste.
-
AFAIK kannst du mit Boost.Function genau das was du willst, nämlich Funktionen mit mehreren Parametern verwenden.
http://www.boost.org/doc/libs/1_39_0/doc/html/function.html
-
buergi schrieb:
[...]
zwar alle Methoden abdecke die irgendeinen einzigen Parameter verlangen, der allerdings weder const noch & etc sein darf.Wieso nicht? Was verpasse ich denn da?, Dann ist A eben ein "const T&", oder "T&" für irgend ein nicht-Referenz-Typ T.
buergi schrieb:
Ich kann damit also z.B. nicht aufrufen
Obj::foo(const string& str) { cout << str << endl; } // [...] string s("foobar"); objlist.apply(&Obj::foo,s);A = const string&
Und? Wo ist das Problem?
Ich hab's jetzt nicht ausprobiert und vielleicht irre ich mich jetzt auch total. Aber ich sehe hier jetzt kein Problem.Gruß,
SP
-
@buergi
Ich verstehe den Sinn deiner Klasse nicht. Genau dafür gibt es std::for_each und boost::bind.std::list<Foo*> li; std::for_each(li.begin(), li.end(), boost::bind(&Foo::bar, _1)); std::for_each(li.begin(), li.end(), boost::bind(&Foo::baz, _1, arg1, arg2));
-
Sebastian Pizer schrieb:
Diese Coll-Klasse bringt Dir irgendwie nicht viel. Du kannst auch direkt eine std::list<> nehmen und einfach for_each aufrufen. Damit bist Du auch viel flexibler.
Dass Du Zeiger speicherst, ignoriere ich jetzt mal.
#include <iostream> #include <list> #include <algorithm> #include <functional> using namespace std; class dings { public: explicit dings(int zahl) : z(zahl) {} void transmogrify() const {cout << z;} private: int z; }; int main() { list<dings> l; l.push_back(dings(1)); l.push_back(dings(2)); l.push_back(dings(3)); for_each(l.begin(), l.end(), mem_fun_ref(&dings::transmogrify)); cout << '\n'; }Gruß,
SPmal was in kürzer
struct Foo{ void operator()(int i){ /* work */ }; // <- der nice operator :D }; Foo foo; for_each(l.begin(), l.end(), foo); // <- change, kürzer und einfacher ihmoTemplate ua. mal weggelassen, wollte nur das Konzept vorstellen.
-
Hallo
erst mal danke für die vielen AntwortenSebastian Pizer schrieb:
Wieso nicht? Was verpasse ich denn da?, Dann ist A eben ein "const T&", oder "T&" für irgend ein nicht-Referenz-Typ T.
[...]
A = const string&Hm ich meine beim ausprobieren, dass ein template A eben nicht darauf passt, sonst hätt ja alles funktioniert und ich wär zufrieden gewesen und hätte diesen Thread nicht angefangen. Hab unten nochmal Codeschnipsel mit den Compiler Fehlern angehängt.
@Sebastian+kung
wie gesagt for_each mit den std::bind1st etc kann ja nur maximal 2 Parameter übergeben soweit ich gesehn habe.@hustbaer+Somelier
hustbaer schrieb:
Ich verstehe den Sinn deiner Klasse nicht.Genau dafür gibt es std::for_each und boost::bind.
Sinn soll sein, dass die Klasse auch andere Tätigkeiten übernimmt, die ich hier jetzt ausgelassen habe, z.B. wird ein Element nur hinzugefügt wenn es nicht schon in der Liste existiert (klar da gibts sicher auch was von Boost Allmächtig)
boost::bind, ja genial das is ja wirklich genau was ich will. Nachdem ich gestern meine gesamt Festplatte aufgeräumt habe um die 37.3 GB freizuschaufeln, die man zum boost builden braucht und nach stundenlanger Rechenzeit habe ich jetzt festgestellt, die header reichen für boost::bind
Naja whatever hier nochmal Codeschnipsel, boost::bind funzt, Zugriff auf Referenzen etc über Templates klappt nicht (klar über void apply( R (T::*method) (A) , A& a) würds funzen aber das ist bei mehreren Parametern keine Alternative, da gibts ja zig Kombinationen)
#include <iostream> #include <list> #include <boost/bind.hpp> class Obj { public: Obj(int _x) : x(_x) {}; void p(int a) { std::cout << x << ":p(" << a << ")" << std::endl; }; void p_p(int* a) { std::cout << x << ":p_p(" << *a << ")" << std::endl; }; void p_cp(const int* a) { std::cout << x << ":p_cp(" << *a << ")" << std::endl; }; void p_r(int& a) { std::cout << x << ":p_r(" << a << ")" << std::endl; }; void p_cr(const int& a) { std::cout << x << ":p_cr(" << a << ")" << std::endl; }; protected: int x; }; template <class T> class Coll { public: void add(T* t) { m_list.push_back(t); } template <class R,class A> void apply( R (T::*method) (A) , A a) { typename std::list<T*>::iterator it; for ( it=m_list.begin(); it != m_list.end(); it++ ) ((*it)->*method)(a); } //protected: (nich gut) std::list<T*> m_list; }; int main() { Coll<Obj> c; c.add(new Obj(1)); c.add(new Obj(2)); c.add(new Obj(3)); int a=1; c.apply(&Obj::p,a); c.apply(&Obj::p_p,&a); // c.apply(&Obj::p_r,a); // error: no matching function for call to `Coll<Obj>::apply(void (Obj::*)(int&), int&)' // c.apply(&Obj::p_cr,a); // error: no matching function for call to `Coll<Obj>::apply(void (Obj::*)(const int&), int&)' // c.apply(&Obj::p_cp,&a); // error: no matching function for call to `Coll<Obj>::apply(void (Obj::*)(const int*), int*)' // zugriff auf m_list, nich gut! nur zum testen std::for_each(c.m_list.begin(), c.m_list.end(), boost::bind(&Obj::p_cr, _1, a)); return 0; }Also nochmal vielen Dank, denk ich werds mit boost::bind und for_each machen, des scheint ja gut zu klappen. Wird zeit, dass ich mich in boost einlese.
Gruß
buergi
-
buergi schrieb:
Sebastian Pizer schrieb:
Wieso nicht? Was verpasse ich denn da?, Dann ist A eben ein "const T&", oder "T&" für irgend ein nicht-Referenz-Typ T.
[...]
A = const string&Hm ich meine beim ausprobieren, dass ein template A eben nicht darauf passt, sonst hätt ja alles funktioniert und ich wär zufrieden gewesen und hätte diesen Thread nicht angefangen. Hab unten nochmal Codeschnipsel mit den Compiler Fehlern angehängt.
Sicher, dass es nicht vielleicht eine const member function war?
class A { public: void foo(); void bar() const; }; void xxx() { void(A::*pmf1)() = &A::foo; void(A::*pmf2)() [b]const[/b] = &A::bar; }Das ätzende bei Elementfunktionen ist, dass man die Qualifizierungen für this (const, volatile und demnächst auch & und &&) nicht generisch behandeln kann. boost::bind und std::mem_fun behandeln also zwei Sonderfälle: non-const member functions und const member functions. Wenn demnächst (C++1x) noch die ref-qualifier dazukommen, sollte man mindestens 4 Kombinationen behandeln können: nix, const, &, const&.
-
so sry war mit Klausurvorbereitung beschäftigt da war grad keine Zeit für C++.
Sebastian Pilzer schrieb:
Sicher, dass es nicht vielleicht eine const member function war?
ich habe doch in meinem letzten Post unten den Codeschnipsel angehängt. Der sollte kompilierbar sein, aber wenn man die auskommentierten Zeilen mit nimmt kommen bei mir die angegebenen Fehlermeldungen.
Aber ist auch egal mit Boost gehts ja.Danke nochmal. Gruß
buergi