2010-04-23 10 views
35

Poczytałem sekcję 13.5 po refuting the notion, że wbudowane operatory nie biorą udziału w przeciążeniu i zauważyłem, że nie ma sekcji o operator->*. Jest to tylko ogólny operator binarny.Czy wolny operator -> * przeładowuje zło?

Jego bracia, operator->, operator*, i operator[], wszyscy muszą być niestatycznymi funkcjami członka. Wyklucza to definicję przeciążenia funkcji swobodnej dla operatora, zwykle używanego do uzyskania odniesienia z obiektu. Ale niezbyt często operator->* jest pominięty. W szczególności, operator[] ma wiele podobieństw. Jest binarny (brakowało mu złotej szansy na uczynienie go n-ary) i akceptuje jakiś kontener po lewej i jakiś rodzaj lokalizatora po prawej. Jego sekcja zasad specjalnych, 13.5.5, wydaje się nie mieć żadnego rzeczywistego skutku, z wyjątkiem zakazu wolnych funkcji. (I to nawet ograniczenie wyklucza poparcia dla przemienności!)

Tak więc, na przykład, jest to perfectly legal:

#include <utility> 
#include <iostream> 
using namespace std; 

template< class T > 
T & 
operator->*(pair<T,T> &l, bool r) 
    { return r? l.second : l.first; } 

template< class T > 
T & operator->*(bool l, pair<T,T> &r) { return r->*l; } 

int main() { 
     pair<int, int> y(5, 6); 
     y->*(0) = 7; 
     y->*0->*y = 8; // evaluates to 7->*y = y.second 
     cerr << y.first << " " << y.second << endl; 
} 

Łatwo znaleźć zastosowania, ale składnia alternatywa raczej nie będzie tak źle. Na przykład, skalowane indeksów dla vector:

v->*matrix_width[2][5] = x; // ->* not hopelessly out of place 

my_indexer<2> m(v, dim); // my_indexer being the type of (v->*width) 
m[2][5] = x; // it is probably more practical to slice just once 

Czy komisja standardy zapomnieć, aby temu zapobiec, było to uważane za brzydkie niepokoić, czy są przypadki użycia świata rzeczywistego?

+0

punkt danych: Coneau (http://www.comeaucomputing.com/tryitout/) odrzuca kod nawet po usunąłem '' a pierwszy operator: 'błąd: żaden operator „-> * "pasuje do tych argumentów" – sbi

+1

@sbi: Comeau było pierwszym miejscem, które odwiedziłem, ale nadal mam otwartą kartę ... Jedyny kod, który wprowadziłem przed przejściem do GCC 4.5, to "struct x {int y;}; int & ope rator -> * (x & l, int r) {return l.y; } void f() { x q; int i i = q -> * 3; } "- i zwraca sukces dla głównego przykładu minus wszystko zależne od tego pierwszego przeciążenia zależnego od typu. – Potatoswatter

+0

Nie wiem, dlaczego operator -> * może być przeciążony w ten sposób, ale na pewno wygląda brzydko! trzymaj się z dala od tego samego powodu, co przeciążenie przecinkiem - nie wygląda na intuicyjny C++ – AshleysBrain

Odpowiedz

2

Podczas kilkudniowego wyszukiwania znalazłem więcej osób pytających o to, czy kiedykolwiek używa się operator->* niż aktualnych sugestii.

Kilka miejsc sugeruje T &A::operator->*(T B::*). Nie jestem pewien, czy to odzwierciedla zamiar projektanta, czy też błędne wrażenie, że wbudowany jest T &A::operator->*(T A::*). Nie jest to tak naprawdę związane z moim pytaniem, ale daje pojęcie o głębi, którą znalazłem w literaturze online.

Pojawiła się wzmianka o "D & E 11.5.4", która, jak przypuszczam, to Design and Evolution of C++. Być może zawiera wskazówkę. W przeciwnym razie po prostu dojdę do wniosku, że jest to trochę bezużyteczna brzydota, która została przeoczona przez normalizację i większość pozostałych.

Edytuj Zobacz poniżej, aby zapoznać się z cytatem z oferty D &.

Aby umieścić to ilościowo, ->* jest najbardziej ścisłym operatorem powiązania, który może być przeciążony przez funkcję wolną. Wszystkie przeciążenia operatorów Postfiksem i Jednoargumentowymi przeciążeniami wymagają niestatycznych sygnatur funkcji członków. Następnym priorytetem po operatorów jednoargumentowych są odlewy w stylu C, które można uznać za odpowiadające funkcjom konwersji (operator type()), które również nie mogą być wolnymi funkcjami. Potem przychodzi ->*, a następnie mnożenie. ->* mogło być jak [] lub podobnie jak %, mogły one pójść w obie strony i wybrały ścieżkę EEEEEEVIL.

+0

Czy to odpowiada na twoje pytanie? Nie wiedziałem, że możemy przyjąć naszą własną odpowiedź :) –

+0

@Ventente: Rozważałem zaakceptowanie twojej, ale nadal błędnie mówi, że nie-członek 'operator->' jest dozwolony i uzasadnienie (lub 2) zezwalające 'operatorowi + = 'ale odrzucanie' operator = 'zostało wypracowane w komentarzach. I tak naprawdę nie szukałem (dis) porozumienia, pytanie brzmiało: * Czy komitet ds. Norm zapomniał o tym zapobiec, czy uznano, że jest zbyt brzydki, aby mu to przeszkadzać, czy też istnieją przypadki rzeczywistego użycia? *, Co jest kwestią znajdowanie pewnych odniesień lub dowodów na poparcie jednego z wniosków. – Potatoswatter

+0

Nie jestem naprawdę zadowolony z tej odpowiedzi, ale zostałem zachęcony do głosowania (może być niegrzecznie pozostawić otwarte pytanie bez odpowiedzi tak długo) i wydaje się, że brakuje dowodów, które wspierałyby wniosek, że został przeoczony. – Potatoswatter

1

Standard (Projekt roboczy 2010-02-16, § 5.5) mówi:

The result of an ->* expression is an lvalue only if its second operand is a pointer to data member. If the second operand is the null pointer to member value (4.11), the behavior is undefined.

Można to zachowanie się dobrze zdefiniowane. Na przykład sprawdź, czy jest to wskaźnik zerowy i obsłużyć tę sytuację. W związku z tym jest to słuszna decyzja, aby standard pozwolił -> * przeładować.

+2

Klauzula 5 nie ma zastosowania do przeciążeń, na przykład przeciążenie 'operator /' może definiować zachowanie dla drugiego argumentu wynoszącego zero, i można go używać zgodnie z prawem: – Potatoswatter

6

Zgadzam się z tobą, że istnieje niespójność w standardzie, Nie pozwala to na przeciążenie operator[] z funkcjami nieczłonkowskimi i zezwala na operator->*. Z mojego punktu widzenia operator[] jest do tablic jako operator->* jest do structs/classes (getter). Członkowie tablicy są wybierani za pomocą indeksu. Członkowie struktury są wybierani za pomocą wskaźników członków.

Najgorsze jest to, że możemy mieć pokusę, aby wykorzystać ->* zamiast operator[] aby uzyskać tablicę podobnego elementu

int& operator->*(Array& lhs, int i); 

Array a; 

a ->* 2 = 10; 

Jest też inna możliwość niespójność. Możemy użyć funkcji non-member do przeciążenia operator+= i wszystkich operatorów formularza @=) i nie możemy tego zrobić dla operator=.

ja naprawdę nie wiem, co jest uzasadnienie dokonać następujących czynności prawnej

struct X { 
    int val; 
    explicit X(int i) : val(i) {} 
}; 
struct Z { 
    int val; 
    explicit Z(int i) : val(i) {} 
}; 
Z& operator+=(Z& lhs, const X& rhs) { 
    lhs.val+=rhs.val; 
    return lhs; 
} 

Z z(2); 
X x(3); 
z += x; 

i zabraniając

Z& operator=(Z& lhs, const X& rhs) { 
    lhs.val=i; 
    return lhs; 
} 

z = x; 

Niestety nie odpowiedzieć na Twoje pytanie, ale dodając jeszcze więcej zamieszania.

+1

'operator ->' i 'operator @ =' muszą być funkcjami składowymi, ponieważ kompilator może obsługiwać dowolne funkcje, ale to z pewnością jest niestandardowe – Potatoswatter

+0

operator @ = może być przeciążony przez nie-członka funkcje: patrz norma –

+2

W §13.5.3 nie rozróżnia się operatorów przypisania: "operator przypisania musi być implementowany przez niestatyczny element er działa z dokładnie jednym parametrem. "§5.17 mówi" Istnieje kilka operatorów przypisania, z których wszystkie grupują od prawej do lewej. "O ile nie ma czegoś, czego mi brakuje,' operator = 'nie powinien być wyjątkowy. – Potatoswatter

13

Najlepszym przykładem, o którym mam świadomość, jest Boost.Phoenix, co powoduje przeciążenie tego operatora w celu wdrożenia dostępu do leniwego elementu.

Dla osób niezaznajomionych z Phoenix, jest to niezwykle ładne biblioteka dla podmiotów budowlanych (lub obiektów funkcji), które wyglądają jak normalne wyrażeń:

(arg1 % 2 == 1)  // this expression evaluates to an actor 
       (3); // returns true since 3 % 2 == 1 

// these actors can also be passed to standard algorithms: 
std::find_if(c.begin(), c.end(), arg1 % 2 == 1); 
// returns iterator to the first odd element of c 

Osiąga powyższe przeciążenia operator% i operator==. - zastosowano wobec aktora arg1 tych operatorów, którzy zwrócą kolejnego aktora. Zakres wyrażeń, które mogą być budowane w ten sposób jest ekstremalne:

// print each element in c, noting its value relative to 5: 
std::for_each(c.begin(), c.end(), 
    if_(arg1 > 5) 
    [ 
    cout << arg1 << " > 5\n" 
    ] 
    .else_ 
    [ 
    if_(arg1 == 5) 
    [ 
     cout << arg1 << " == 5\n" 
    ] 
    .else_ 
    [ 
     cout << arg1 << " < 5\n" 
    ] 
    ] 
); 

Po zostały użyciem Phoenix na krótki czas (nie, że kiedykolwiek wrócić) można spróbować czegoś takiego:

typedef std::vector<MyObj> container; 
container c; 
//... 
container::iterator inv = std::find_if(c.begin(), c.end(), arg1.ValidStateBit); 
std::cout << "A MyObj was invalid: " << inv->Id() << std::endl; 

Co się nie powiedzie, bo oczywiście aktorzy Feniksa nie mają członka ValidStateBit. Phoenix omija to przez przeciążenie operator->*:

(arg1 ->* &MyObj::ValidStateBit)    // evaluates to an actor 
           (validMyObj); // returns true 

// used in your algorithm: 
container::iterator inv = std::find_if(c.begin(), c.end(), 
     (arg1 ->* &MyObj::ValidStateBit) ); 

operator->* „s argumenty są:

  • LHS: aktora powracającego MyObj *
  • RHS: adres członka

Zwraca wartość aktor, który ocenia LHS i szuka określonego członka w nim.(NB: Naprawdę, naprawdę chcesz się upewnić, że arg1 zwraca - nie widzisz ogromnego błędu szablonu, dopóki nie uzyskasz czegoś złego w Phoenix. Ten mały program wygenerował 76 738 znaków bólu (Zwiększ 1.54, gcc 4.6):

#include <boost/phoenix.hpp> 
using boost::phoenix::placeholders::arg1; 

struct C { int m; }; 
struct D { int n; }; 

int main() { 
    (arg1 ->* &D::n) (new C); 
    return 0; 
} 
+0

Doskonale, dziękuję za udostępnienie! Jednak to użycie sugeruje implementację jako kanoniczną funkcję członka, podczas gdy pytanie dotyczy bardziej szczegółowo funkcji nie-członkowskich (wolnych). – Potatoswatter

+1

Sprawdziłem: Implementacja funkcji Boost używa funkcji nie należącej do elementu w oddzielnej przestrzeni nazw anty-ADL. Być może jest to zrobione dla ogólnej najlepszej praktyki i jednolitości z innymi operatorami. – Potatoswatter

Powiązane problemy