[template] functor
-
Und um das Ganze noch etwas voranzutreiben:
template<class C, typename E> struct fkt_compare { typedef M C::(*MemFktPtr)(); compare(MemFktPtr ptr) : mfp_(ptr); bool operator()(C& l, C& r) {return l.(*mp_)() < m.(*mp_)();} private: MemPtr mfp_; }; template <class C, typename M> //template argument deduction again fkt_compare<C,M> make_compare(M C::(*ptr)()) {fkt_compare<C,M> fcmp(ptr); return fcmp}Damit kann make_compare auch ein Funktionspointer uebergeben werden, gleichzeitiges Ueberladen von Funktionstemplate mit template argument deduction

-
Ja, das geht - und das hat LordJaxom schon genannt. Bei meiner Variante ist es allerdings nicht so einfach, die argument deduction auszunutzen, weil der letzte Parameter nicht zur Compilezeit ermittelt werden kann (und
template<M K::*Member>...scheitert aran, daß der Compiler M und K nicht kennt).
-
@CStoll: meinst du das?
class Klasse{ public: Klasse(int val1, int val2 ) : val1(val1),val2(val2) {} // int getVal1() const { return val1; } // int getVal2() const { return val2; } //private: int val1, val2; }; template<typename K, typename M, M (K::*Member)> struct compareMember { bool operator()(const K &r, const K &l) { return r.*Member < l.*Member; } }; int main(){ list<Klasse> l; l.push_back(Klasse(10,10)); l.push_back(Klasse(1,5)); l.push_back(Klasse(50,3)); sort(l.begin(),l.end(),compareMember<Klasse, int, &Klasse::val1>()); };endet in folgendes compilerfehlermeldungen:
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::list<_Ty>::_Iterator<_Secure_validation>'
with
[
_Ty=Klasse,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : see declaration of 'std::operator -'
F:\LBM_subversion\source\lbmd2q9\a2pII\testcases\inputfileSerial\main.cpp(57) : see reference to function template instantiation 'void std::sort<std::list<_Ty>::_Iterator<_Secure_validation>,compareMember<K,M,Member>>(_RanIt,_RanIt,_Pr)' being compiled
with
[
_Ty=Klasse,
_Secure_validation=true,
K=Klasse,
M=int,
Member=pointer-to-member(0x0),
_RanIt=std::list<Klasse>::_Iterator<true>,
_Pr=compareMember<Klasse,int,pointer-to-member(0x0)>
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::list<_Ty>::_Iterator<_Secure_validation>'
with
[
_Ty=Klasse,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : see declaration of 'std::operator -'
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::list<_Ty>::_Iterator<_Secure_validation>'
with
[
_Ty=Klasse,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : see declaration of 'std::operator -'
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2784: 'reverse_iterator<_RanIt>::difference_type std::operator -(const std::reverse_iterator<_RanIt> &,const std::reverse_iterator<_RanIt2> &)' : could not deduce template argument for 'const std::reverse_iterator<_RanIt> &' from 'std::list<_Ty>::_Iterator<_Secure_validation>'
with
[
_Ty=Klasse,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\xutility(1856) : see declaration of 'std::operator -'
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2676: binary '-' : 'std::list<_Ty>::_Iterator<_Secure_validation>' does not define this operator or a conversion to a type acceptable to the predefined operator
with
[
_Ty=Klasse,
_Secure_validation=true
]
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3261) : error C2780: 'void std::_Sort(_RanIt,_RanIt,_Diff)' : expects 3 arguments - 4 provided
C:\Program Files\Microsoft Visual Studio 8\VC\include\algorithm(3085) : see declaration of 'std::_Sort'
-
sort() erwartet Random-Access-Iteratoren, aber list<> hat "nur" bidirektionale Iteratoren (und der Compiler scheitert dabei, einen operator- für list-Iteratoren zu finden).
Also entweder du verwendest einen anderen Container (vector<> oder deque<>) oder du nutzt die list-Methode sort():
l.sort(compareMember<Klasse, int, &Klasse::val1>());
-
ich vollhonk... klaro... war vor lauter deduction ganz verwirrt...
-
okay. mit den membern klappt es nun. sehr gut. aber der methoden vergleich scheitert noch:
class Klasse{ public: Klasse(double val1, double val2 ) : val1(val1),val2(val2) {} double getVal1() const { return val1; } double getVal2() { return val2; } private: double val1, val2; }; template<typename K, typename M, M (K::*fct)()> struct compareMethods{ bool operator()(const K& r, const K& l) { return r.(*fct)() < l.(*fct)(); } }; int main(int argc, char** argv) { list<Klasse> l; l.push_back(Klasse(10,10)); l.push_back(Klasse(1,5)); l.sort(compareMethods<Klasse, double, &Klasse::getVal1 >() ); l.sort(compareMethods<Klasse, double, &Klasse::getVal2 >() ); }Fehler:
//Fuer l.sort( compareMethods<Klasse, double, &Klasse::getVal1 >() ):
1>main.cpp error C2440: 'specialization' : cannot convert from 'overloaded-function' to 'double (__thiscall Klasse::* )(void)'
//Fuer l.sort(compareMethods<Klasse, double, &Klasse::getVal2 >() ):
1>main.cpp error C2059: syntax error : '(' bei { return r.(*fct)() < l.(*fct)(); 1>while compiling class template member function 'bool compareMethods<K,M,fct>::operator ()(const K &,const K &)'
1> with
1> [
1> K=Klasse,
1> M=double,
1> fct=Klasse::getVal2
1> ]
1>main.cpp (56) : see reference to class template instantiation 'compareMethods<K,M,fct>' being compiled
1> with
1> [
1> K=Klasse,
1> M=double,
1> fct=Klasse::getVal2
1> ]
-
Zum ersten Fall: <ratemodus>fct ist ein Pointer zu einer non-const Funktion. Ich vermute, dass der Compiler Probleme damit hat, ihn mit einen const K anzuwenden. template<...., M (K::*fct)() const> oder sowas in der Art koennte evtl. helfen </ratemodus>
Zum zweiten Fall: Die Klammern um *fct waren Bloedsinn von mir. Dadurch wird der Member-Pointer_Zugriffsoperator .* zerlegt. Mein Fehler, sry...
alsoreturn r.*fct() < l.*fct();
-
auch nach der entfernung der klammern: gleicher fehler...
-
Gnarf ^^
hab mal in mein schlaues buch geschut, da wurden pointer-to-mem-fun's so aufgerufen:return (r.*fct)() < (l.*fct)();Vermutlich muss der linke AUsdruck geklammert werden, damit er erst dessen Rueckgabewert bestimmt (naemlich die Funktion fuer ein spezielles Obejkt), und danach erst den operator() drauf anwendet...
-
kannst du es dann kompilieren? denn dann bekomme ich diesen Fehler:
error C2440: 'argument' : cannot convert from 'const Klasse *' to 'Klasse *const '
Conversion loses qualifiers
-
Wo hast Du das const hingepackt? Folgendes Beispiel kompiliert hier: (Änderungen zu Deinem letzten vollständigen Beispiel sind markiert)
class Klasse { public: Klasse(double val1, double val2 ) : val1(val1),val2(val2) {} double getVal1() const { return val1; } double getVal2() const { return val2; } // <- hier const private: double val1, val2; }; template<typename K, typename M, M (K::*fct)() const> // <- hier const struct compareMethods { bool operator()(const K& r, const K& l) const // <- hier const (zwar nicht zwingend notwendig, aber eleganter) { return (r.*fct)() < (l.*fct)(); } // <- Klammersetzung }; using namespace std; int main(int argc, char** argv) { list<Klasse> l; l.push_back(Klasse(10,10)); l.push_back(Klasse(1,5)); l.sort( compareMethods<Klasse, double, &Klasse::getVal1 >() ); l.sort( compareMethods<Klasse, double, &Klasse::getVal2 >() ); }
-
so funktionierts. leider kann ich grad nicht sagen, woran es bei mir gehapert hat. Danke @all.
-
Wie schon gesagt, das const im Template parameter ist wichtig, denn sonst ist fct ein pointer auf einen nonconst member function und kann fuer const "K"s nicht aufgerufen werden. Bei pointer to membern muss immer noch ein zusaetzliches Argument uebergeben werden, naemlich wessen Member das nun sein soll. Das wird eben ueber den Operator .* bzw. ->* erledigt. Das Konstrukt
(r.*fct)bedeutet soviel wie "Uebergebe r als aktuelles Objekt an fct." Da fct selbst zwar ein konstanter pointer ist, aber auf ein nicht konstantes Objekt verweist, versucht der Compiler, aus dem konstanten r ein nichtkonstantes zu machen, damit fct angewendet werden kann. Zu recht beschwert er sich dann.
Um beides machen zu koennen, muesste das Ganze dann so aussehen:template<class K, typename M, M (K::*fct)()> // Allgemeiner Fall struct compareMethods { bool operator()(K& r, K& l) const // da fct nicht const ist, kann auch K nicht const sein. das const hinter der deklaration besagt dass compareMethods const sein kann { return (r.*fct)() < (l.*fct)(); } }; template<class K, typename M> struct compareMethods<K, M, M (K::*fct)() const> //partielle Spezialisierung fuer const pointers { bool operator()(const K& r, const K& l) const //hier koennen die K's auch const sein, muessen sie aber nicht (const hinzufuegen geht ja problemlos) { return (r.*fct)() < (l.*fct)(); } };Damit kannst du sowohl Funktionspointer auf konstante als auch auf nichtkonstante Memberfunktionen benutzen. bei letzteren kannst du allerdings keine konstanten Parameter uebergeben (klar, man kann keine nichtkonstanten Funktionen fuer konstante Objekte aufrufen)
}
-
@pumuckl:
genau sowas hatte ich auch versucht. aber leider gibt es imme räger bei der spezielisierung des funktionpointers.class Klasse { public: Klasse(double val1, double val2 ) : val1(val1),val2(val2) {} double getVal1() { return val1; } double getVal2() const { return val2; } // <- hier const private: double val1, val2; }; template<class K, typename M, M (K::*fct)()> // Allgemeiner Fall struct compareMethods { bool operator()(K& r, K& l) const // da fct nicht const ist, kann auch K nicht const sein. das const hinter der deklaration besagt dass compareMethods const sein kann { return (r.*fct)() < (l.*fct)(); } }; template<class K, typename M> /*Z43*/struct compareMethods<K, M, M (K::*fct)() const > //partielle Spezialisierung fuer const pointers { bool operator()(const K& r, const K& l) const //hier koennen die K's auch const sein, muessen sie aber nicht (const hinzufuegen geht ja problemlos) { return (r.*fct)() < (l.*fct)(); } }; int main(int argc, char** argv) { std::list<Klasse> l; l.push_back(Klasse(10,10)); l.push_back(Klasse(1,5)); l.push_back(Klasse(50,3)); l.push_back(Klasse(4,6)); l.push_back(Klasse(0,8)); l.sort( compareMethods<Klasse, double, &Klasse::getVal1 >() ); l.sort( compareMethods<Klasse, double, &Klasse::getVal2 >() ); }führt zu:
main.cpp(43) : error C2143: syntax error : missing ')' before '<tag>::*'
main.cpp(44) : error C2143: syntax error : missing ';' before '{'
main.cpp(46) : error C2143: syntax error : missing ';' before '{'
main.cpp(47) : error C2143: syntax error : missing ';' before '}'
main.cpp(49) : error C2146: syntax error : missing ',' before identifier 'argv'
main.cpp(49) : error C2065: 'argv' : undeclared identifier
main.cpp(49) : error C2143: syntax error : missing ',' before ')'
main.cpp(50) : error C2143: syntax error : missing ';' before '{'
-
Hm, ich seh grade, das ist ja garkeine Spezialisierung, sondern ein voellig anderer Templateparameter. so aehlich wie template<bool> keine Spezialisierung von template<int> ist. Die beiden Zeiger sind einfach verschiedene Typen. Spezialisiern kann man nur, wenn man einen Typ-parameter weiter einschraenkt oder einen Konstantenparameter wie wir ihn hier haben, explizit angibt.
Wenn ich mich recht erinnere geht die "Ueberladung" von Klassentemplates auch nicht, sprich sowas wie
template<class K, typename M, M (K::*fct)()> // Nonconst Fall struct compareMethods; template<class K, typename M, M (K::*fct)() const> // Const Fall struct compareMethods;klappt nicht, weil zwei unterschiedliche Klassentemplates nicht den gleichen Namen haben duerfen.
Bleiben also auch wieder nur zwei verschiedene Klassentemplates compareMethods und compareConstMethods, sowie zwei ueberladene Funktionentemplates, die mit argument deduction das eine oder andere von beiden zurueck geben.
-
Gesammelte Werke aus diesem thread dann wie folgt. Man kann als zusaetzlichen Templateparameter auch noch die Art des Vergleichs bestimmen. Wird ja nicht immer operator< sein:
template <class C, typename M, class Comparator> //for member variables sruct compareMem { typedef const M C::*MemPtr; compare(MemPtr ptr, Comparator c) : mp_(ptr), comp(c); bool operator()(const C& l, const C& r) {return comp(l.*mp_,m.*mp_);} private: MemPtr mp_; Comparator comp; }; template<class C, typename E, class Comparator> //for non const methods struct compareMethod { typedef M C::(*MemFktPtr)(); compare(MemFktPtr ptr, Comparator c) : mfp_(ptr), comp(c); bool operator()(C& l, C& r) {return comp((l.*mfp_)(), (m.*mfp_)());} private: MemPtr mfp_; Comparator comp; }; template<class C, typename E, class Comparator> //for const methods struct compareConstMethod { typedef M C::(*ConstMemFktPtr)() const; compare(ConstMemFktPtr ptr, Comparator c) : cmfp_(ptr), comp(c); bool operator()(const C& l, const C& r) {return (comp(l.*cmfp_)(), (m.*cmfp_)());} private: MemPtr cmfp_; Comparator comp; }; //overloads for custom comparator template <class C, typename M, class Comparator> //template argument deduction compareMember<C,M,Comparator> make_compare(M C::*ptr, Comparator c) {compareMember<C,M> cmp(ptr,c); return cmp} template <class C, typename M, class Comparator> //template argument deduction again compareMethod<C,M,Comparator> make_compare(M C::(*ptr)(), Comparator c) {compareMethod<C,M> fcmp(ptr,c); return fcmp} template <class C, typename M, class Comparator> //template argument deduction again compareConstMethod<C,M,Comparator> make_compare(M C::(*ptr)() const, Comparator c) {compareConstMethod<C,M,Comparator> fcmp(ptr,c); return fcmp} //overloads for default comparator std::less<M> template <class C, typename M> //template argument deduction compareMember<C,M,Comparator> make_compare(M C::*ptr) {return make_compare(ptr, std::less<M>()} // etc.Allmaehlich reichts fuer n eigenen Header "comparemembers.h"

-
*lach* genauso isses. was lange währt, wird endlich gut

was mir an der letzteren nicht gefällt ist, dass jedes mal eine member variable angelegt werden muss.
und kannst du mir in kurzen worten dieses deduction erklären? der begriff fiel hier schon öfter, was genau ist das problem für den compiler?
-
Argument Deduction bedeutet, daß der Compiler selber herausfinden kann, was er für die Template-Parameter einsetzen muss. Das heißt, wenn du irgendwo 'make_compare(&klasse::memb)' stehen hast, dann findet er erstens heraus, auf welche der drei Überladungen er zugreifen soll (Member, const-Getter oder normaler Getter) und kann zweitens auch selber herausfinden, mit welchen Template-Parametern er sie verwenden soll.
(das erspart dir als Programmierer, die Typen selber raussuchen zu müssen)
-
Das ist eher kein Problem fuer den Compiler, sondern eine Ersparnis fuer den Programmierer (und viel mehr).
Nehmen wir an, du hast ein Funktionstemplate
template<typename T> void f(T param){}Dann muesstest du beim Aufruf eigentlich immer die Templateparameter angeben, z.B.
int i =1; double d = 2.0; f<int>(i); f<double>(d);Template argument deduction bedeutet, dass der Compiler sich selbst ueberlegen kann, fuer welchen Typ er das Funktionstemplate initialisieren muss, schliesslich weiss er ja, von welchem Typ der Funktionsparameter ist. Dann muss der Templateparameter nicht mehr angegeben werden:
f(i); //i ist ein int, also f<int> f(d); //d ist double, also f<double>std::sort selbst macht Gebrauch davon, denn sowohl der Iteratortyp als auch der Comparatortyp sind Templateparameter, die allerdings nicht angegeben werden muessen, weil sie ja als Funktionsparameter uebergeben werden.
In meinem Beispiel oben kann der Compiler aus einem einzigen Funktionsparameter beide Templateparameter herleiten, da in dem Pointer ja sowohl der Typ der Klasse als auch der Typ der Membervariable drinsteckt.
-
CStoll schrieb:
(das erspart dir als Programmierer, die Typen selber raussuchen zu müssen)
...was sehr hilfreich sein kann (oder weiss jemand auswendig, was für einen Rückgabetyp std::tr1::bind(&Klasse::member, this) hat?)