2014-12-24 24 views
11

Mam własną implementację inteligentnego wskaźnika, a teraz próbuję rozwiązać problem wywoływania funkcji składowej przez jej wskaźnik. Nie zapewniam żadnej funkcji get() - (tak naprawdę zapewniam operator->, ale nie chcę go używać w tym celu).Przeciążanie operatora -> * w C++

Moje pytanie brzmi: jak powinien wyglądać podpis i typ zwrotu w postaci operator->*?

+9

Dla @OP: [tu jest papier] (http://aristeia.com/Papers/DDJ_Oct_1999.pdf), który opisuje ** ** dokładnie, co chcesz. –

+2

"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. –

+0

Wskaźnik do członka, przez C++ Najczęściej zadawane pytania: http://www.parashift.com/c++-faq/dotstar-vs-arrowstar.html –

Odpowiedz

6

Dla kompletności, tutaj jest kompletny, compilable, minimal przykład, mocno inspirowany tym paper I've linked to i okrojona wraz z niewielką demo użytkowania, aby zacząć grę z tym:

#include <memory> 
#include <iostream> 
#include <utility> 


// Our example class on which we'll be calling our member function pointer (MFP) 
struct Foo { 
    int bar() { 
     return 1337; 
    } 
}; 

// Return value of operator->* that represents a pending member function call 
template<typename C, typename MFP> 
struct PMFC { 
    const std::unique_ptr<C> &ptr; 
    MFP pmf; 
    PMFC(const std::unique_ptr<C> &pPtr, MFP pPmf) : ptr(pPtr), pmf(pPmf) {} 

    // the 'decltype' expression evaluates to the return type of ((C*)->*)pmf 
    decltype((std::declval<C &>().*pmf)()) operator()() { 
     return (ptr.get()->*pmf)(); 
    } 
}; 

// The actual operator definition is now trivial 
template<typename C, typename MFP> 
PMFC<C, MFP> operator->*(const std::unique_ptr<C> &ptr, MFP pmf) 
{ 
    return PMFC<C, MFP>(ptr, pmf); 
} 

// And here's how you use it 
int main() 
{ 
    std::unique_ptr<Foo> pObj(new Foo); 
    auto (Foo::*pFn)() = &Foo::bar; 
    std::cout << (pObj->*pFn)() << std::endl; 
} 
1

Dwa argumenty dla przeciążonego operatora ->* powinny być 1. obiektem twojej klasy i 2. wskaźnikiem do elementu. W najprostszym przypadku oznacza to, że przeciążony operator powinien być członkiem klasy przyjmującej jeden argument typu wskaźnik do członka, więc na przykład dla wskaźnika do funkcji składowej bez parametrów byłoby:

TYPE operator->*(void (YourClass::*mp)()); 

Typ zwrotu powinien być możliwy do wywołania (w tym znaczeniu, że ma do niego zastosowanie operator()). Najłatwiej pokazać innym klasom - tutaj masz pełny przykład:

struct Caller { 
void operator()() { cout << "caller"; } 
}; 

struct A { 
void f() { cout << "function f"; } 
Caller operator->*(void (A::*mp)()) { return Caller(); } 
}; 

int main() { 
A a; 
void (A::*mp)() = &A::f; 
(a->*mp)(); 
return 0; 
} 

, który wyświetla "dzwoniącego". W prawdziwym świecie trzeba używać szablonów do obsługi różnych typów wskaźników. Więcej szczegółów można znaleźć w Scott Meyer's paper.

2

operator->*() trwa dwa argumenty:

  1. Obiekt, na którym działa.
  2. 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:

  1. Jedna wersja pobiera wskaźnik do elementu niefunkcyjnego, który właśnie zwraca element referencyjny dla podanego wskaźnika.
  2. 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; } 
}; 
Powiązane problemy