operator->*()
trwa dwa argumenty:
- Obiekt, na którym działa.
- Wskaźnik elementu do zastosowania.
Jeśli wskaźnik członu jest po prostu dostępny dla elementu danych, wynik jest prosty: można po prostu zwrócić referencję do elementu. Jeśli jest to funkcja, cóż, rzeczy są nieco bardziej skomplikowane: operator dostępu do elementu musi zamiast tego zwracać obiekt wywoływalny. Obiekt wywoływalny pobiera odpowiednią liczbę argumentów i zwraca typ wskaźnika członkowskiego.
Zdaję sobie sprawę, że oryginalne pytanie jest oznaczone tagiem C++ 03, ale wykonanie "właściwego" wdrożenia C++ 03 to dość długie ćwiczenie pisania: musisz zrobić to, co jest wygodnie wykonane przez szablony variadyczne w kodzie poniżej dla każdej liczby argumentów. Tak więc ten kod używa C++ 11 przede wszystkim do wyraźniejszego pokazania, co jest potrzebne i uniknięcia wykonywania ćwiczeń na klawiaturze.
Oto prosty „inteligentny” wskaźnik definiowania operator->*()
:
template <typename T>
class my_ptr
{
T* ptr;
public:
my_ptr(T* ptr): ptr(ptr) {}
template <typename R>
R& operator->*(R T::*mem) { return (this->ptr)->*mem; }
template <typename R, typename... Args>
struct callable;
template <typename R, typename... Args>
callable<R, Args...> operator->*(R (T::*mem)(Args...));
};
myślę za „właściwe” wsparcie to musi również określić const
wersje: że powinno być dość proste, więc jestem pomijając te. Zasadniczo istnieją dwie wersje:
- Jedna wersja pobiera wskaźnik do elementu niefunkcyjnego, który właśnie zwraca element referencyjny dla podanego wskaźnika.
- Jedna wersja pobierająca wskaźnik do elementu funkcji, który zwraca odpowiedni obiekt
callable
.callable
będzie musiał mieć operatora wywołania funkcji i zastosować go odpowiednio.
Więc, następną rzeczą do określenia jest rodzaj callable
: będzie posiadać wskaźnik do obiektu i wskaźnik do członka i stosuje je na rozmowy:
#include <utility>
template <typename T>
template <typename R, typename... Args>
struct my_ptr<T>::callable {
T* ptr;
R (T::*mem)(Args...);
callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {}
template <typename... A>
R operator()(A... args) const {
return (this->ptr->*this->mem)(std::forward<A>(args)...);
}
};
Dobrze, że to dość prosto Naprzód. Jedynym trudnym bitem jest to, że argumenty, z którymi wywoływany jest operator wywołania funkcji, mogą być innego rodzaju niż te, które dotyczą wskaźnika do elementu. Powyższy kod dotyczy sytuacji, po prostu przekazując je.
Brakujący kawałek jest funkcja fabryka dla powyższego callable
typu:
template <typename T>
template <typename R, typename... Args>
my_ptr<T>::callable<R, Args...> my_ptr<T>::operator->*(R (T::*mem)(Args...)) {
return callable<R, Args...>(this->ptr, mem);
}
OK, to wszystko! Jest to całkiem sporo kodu przy użyciu fantazyjnych szablonów variadic C++ 11. Wpisanie tego materiału w celu dostarczenia go do C++ 03 nie jest czymś, co bym sobie wymyślił. Na plus, I myśleć tych operatorów mogą być funkcje nie-członkowskie. Oznacza to, że mogą one zostać zaimplementowane w odpowiedniej przestrzeni nazw, która zawiera tylko te operatory i pusty typ znacznika, z którego typ inteligentnego wskaźnika dziedziczy. Ponieważ tag-tag jest bazą, operatory zostaną znalezione za pośrednictwem ADL i będą działać dla wszystkich inteligentnych wskaźników. Mogą one, na przykład, użyć operator->()
, aby uzyskać wskaźnik potrzebny do zbudowania callable
.
Korzystanie z C++ 11 jest rzeczywiście dość proste, aby zaimplementować obsługę operator->*()
niezależnie od konkretnego typu inteligentnego wskaźnika. Poniższy kod pokazuje implementację i proste użycie. Korzysta z faktu, że ta wersja może być znaleziona tylko na podstawie ADL (nigdy nie powinieneś mieć dyrektywy lub deklaracji użycia) i że inteligentne wskaźniki prawdopodobnie implementują operator->()
: kod wykorzystuje tę funkcję do uzyskania wskaźnika wskaźnika inteligentnego . Przestrzeń nazw member_access
powinna prawdopodobnie przejść do odpowiedniego nagłówka po prostu dołączonego przez inne inteligentne wskaźniki, które następnie dziedziczą po member_access::member_acccess_tag
(która może być klasą bazową private
(!), Która nadal wyzwala ADL do przejrzenia member_access
).
#include <utility>
namespace member_access
{
struct member_access_tag {};
template <typename Ptr, typename R, typename T>
R& operator->*(Ptr ptr, R T::*mem) {
return ptr.operator->()->*mem;
}
template <typename R, typename T, typename... Args>
struct callable {
T* ptr;
R (T::*mem)(Args...);
callable(T* ptr, R (T::*mem)(Args...)): ptr(ptr), mem(mem) {}
template <typename... A>
R operator()(A... args) const {
return (this->ptr->*this->mem)(std::forward<A>(args)...);
}
};
template <typename Ptr, typename R, typename T, typename... Args>
callable<R, T, Args...> operator->*(Ptr ptr, R (T::*mem)(Args...)) {
return callable<R, T, Args...>(ptr.operator->(), mem);
}
}
template <typename T>
class my_ptr
: private member_access::member_access_tag
{
T* ptr;
public:
my_ptr(T* ptr): ptr(ptr) {}
T* operator->() { return this->ptr; }
};
Dla @OP: [tu jest papier] (http://aristeia.com/Papers/DDJ_Oct_1999.pdf), który opisuje ** ** dokładnie, co chcesz. –
"Pointer-to-member operators" -> * "i". * "Są opisane w standardzie C++ 11 w sekcji 5.5. Istnieją w C++, nawet jeśli nie są powszechnie używane. –
Wskaźnik do członka, przez C++ Najczęściej zadawane pytania: http://www.parashift.com/c++-faq/dotstar-vs-arrowstar.html –