Możemy stworzyć wskaźnik na pola lub metody, korzystając z auto bądź decltype, zamiast pisać skomplikowaną deklarację (w starym stylu). Analogicznie do operatorów odwołujących się do zmiennych obiektu (. oraz ->) istnieją dwa mechanizmy do odwołania się do pola (zmiennej lub metody) przez wskaźnik: .* oraz ->*.
Poniżej ptr_name, jest wskaźnikiem na pole MyClass::name. Dla ptr_name2 skorzystałem z auto dzięki czemu deklaracja znacznie się skróciła.
#include <iostream> using namespace std; struct MyClass { std::string name; }; int main() { MyClass m; std::string MyClass::*ptr_name = &MyClass::name; m.name = "tekst"; MyClass *d = &m; cout << d->*ptr_name << endl; cout << m.*ptr_name << endl; m.*ptr_name = "blow"; cout << m.name << endl; auto ptr_name2 = &MyClass::name; m.*ptr_name2 = "blow up"; cout << m.name << endl; return 0; }Wynik:
tekst tekst blow blow up
Wskaźnik do metody w klasie
Na początek prosty przykład z wołaniem metody bezargumentowej. Standard wprowadza dodatkowe trzy mechanizmy pozwalające na odwołanie się to metody klasy. Są to std::function (klasa szablonowa), std::mem_fn oraz std::bind pozwalająca na wygenerowanie wołanego obiektu ze wskaźnika na funkcję. Ponieważ działam na metodach klasy spodziewam się, że std::mem_fn jest do tego najlepszym mechanizmem (z racji przeznaczenia), ale nie wiem, czy jest jakaś istotna różnica w porównaniu do dwóch pozostałych.#include <algorithm> #include <functional> #include <iostream> struct MyClass { MyClass(std::string s) : name(s) { } std::string info() { std::cout << "info " << name << std::endl; return name; } int fun(char*) { std::cout << "fun " << name << std::endl; return 8; } std::string name; }; int main() { using namespace std::placeholders; std::vector<MyClass> vec {MyClass("a"), MyClass("b"), MyClass("c")}; std::function<std::string (MyClass&)> pinfo1 = &MyClass::info; std::for_each(begin(vec), end(vec), pinfo1); std::for_each(begin(vec), end(vec), std::mem_fn(&MyClass::info)); auto pinfo2 = std::bind(&MyClass::info, _1); std::for_each(begin(vec), end(vec), pinfo2); return 0; }Następny przykład to wołaniem metody (MyClass::fun) posiadającej jeden argument. Na początku deklaracja wskaźnika na funkcję w starym dobrym stylu (pfun1) i jej uproszczona wersja korzystająca z auto (pfun2). Korzystanie z std::function oraz std::mem_fn jest już trochę bardziej skomplikowane i wymaga zastosowania lambdy (nie jestem pewien, czy można sobie to jeszcze jakoś bardziej ułatwić).
Najbardziej przejrzyste rozwiązanie powstało z std::bind dzięki placeholder-om. Warto pamiętać, że korzystając ze wskaźników na funkcję (tak przy deklaracji jak i wywołaniu), należy użyć dodatkowych nawiasów, ponieważ operator nawiasowy ma wyższy priorytet niż operator wskaźnika-na-pole (.*). Ogólne postać:
(Class::*fun)(param) (obj.*fun)(arg)Pełny przykład:
#include <algorithm> #include <functional> #include <iostream> struct MyClass { MyClass(std::string s) : name(s) { } std::string info() { std::cout << "info " << name << std::endl; return name; } int fun(char*) { std::cout << "fun " << name << std::endl; return 8; } std::string name; }; int main() { using namespace std::placeholders; std::vector<MyClass> vec {MyClass("a"), MyClass("b"), MyClass("c")}; int (MyClass::*pfun1)(char*) = &MyClass::fun; std::for_each(begin(vec), end(vec), [&](MyClass& c){ (c.*pfun1)(nullptr); }); auto pfun2 = &MyClass::fun; std::for_each(begin(vec), end(vec), [&](MyClass& c){ (c.*pfun2)(nullptr); }); std::function<int(MyClass&, char*)> pfun3 = &MyClass::fun; std::for_each(begin(vec), end(vec), [&](MyClass& c){ pfun3(c, nullptr); }); std::for_each(begin(vec), end(vec), [&](MyClass& c){ std::mem_fn(&MyClass::fun)(c, nullptr); }); std::for_each(begin(vec), end(vec), std::bind(&MyClass::fun, _1, nullptr)); return 0; }A teraz problem, który od dawna chodził mi po głowie, czyli stworzenie funkcji hash-ującej dla unordered_map, bez wrapera w postaci free function. Ponieważ getHash() jest metodą const-ową, std::function musi to uwzględniać w parametrze.
#include <functional> #include <iostream> #include <unordered_map> struct Color { Color(size_t v) : value(v) {} size_t getHash() const { std::cout << "getHash() " << value << std::endl; return value; } private: size_t value; }; int main() { using namespace std::placeholders; constexpr size_t bucket_count = 42; auto equalOp = [] (const Color& l, const Color& r) { return l.getHash() == r.getHash(); }; std::function<size_t(const Color&)> hashFun1 = &Color::getHash; std::unordered_map<Color, std::string, decltype(hashFun1), decltype(equalOp)> m1(bucket_count, hashFun1, equalOp); m1[Color(1)] = "blue"; auto hashFun2 = std::mem_fn(&Color::getHash); std::unordered_map<Color, std::string, decltype(hashFun2), decltype(equalOp)> m2(bucket_count, hashFun2, equalOp); m2[Color(2)] = "red"; auto hashFun3 = std::bind(&Color::getHash, _1); std::unordered_map<Color, std::string, decltype(hashFun3), decltype(equalOp)> m3(bucket_count, hashFun3, equalOp); m3[Color(3)] = "green"; return 0; }
Brak komentarzy:
Prześlij komentarz