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łanie 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(const char* arg) {
std::cout << "obj " << name << ", fun " << arg << 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)(const char*) = &MyClass::fun;
std::for_each(begin(vec), end(vec), [&](MyClass& c){ (c.*pfun1)("pfun1"); });
auto pfun2 = &MyClass::fun;
std::for_each(begin(vec), end(vec), [&](MyClass& c){ (c.*pfun2)("pfun2"); });
std::function<int(MyClass&, const char*)> pfun3 = &MyClass::fun;
std::for_each(begin(vec), end(vec), [&](MyClass& c){ pfun3(c, "pfun3"); });
std::for_each(begin(vec), end(vec),
[&](MyClass& c){ std::mem_fn(&MyClass::fun)(c, "std::mem_fn"); });
std::for_each(begin(vec), end(vec),
std::bind(&MyClass::fun, _1, "std::bind"));
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;
}