Obie (a)
i (b)
powodują niezdefiniowane zachowanie. Wywołanie funkcji składowej za pomocą wskaźnika pustego jest zawsze niezdefiniowanym zachowaniem. Jeśli funkcja jest statyczna, również jest technicznie niezdefiniowana, ale istnieje spór.
Pierwszą rzeczą do zrozumienia jest to, dlaczego odwołanie do pustego wskaźnika jest niezdefiniowanym zachowaniem. W C ++ 03 jest tu właściwie trochę niejednoznaczności.
Mimo że „dereferencjonowanie pustego wskaźnika skutkuje niezdefiniowanym zachowaniem” jest wspomniane w uwagach w obu §1.9 / 4 i §8.3.2 / 4, nigdy nie zostało to wyraźnie powiedziane. (Uwagi są nienormatywne.)
Można jednak spróbować wywnioskować to z § 3.10/2:
Wartość l odnosi się do obiektu lub funkcji.
Podczas dereferencji wynikiem jest lwartość. Wskaźnik zerowy nie odnosi się do obiektu, dlatego kiedy używamy lvalue, mamy niezdefiniowane zachowanie. Problem w tym, że poprzednie zdanie nigdy nie zostało wypowiedziane, więc co to znaczy „używać” lwartości? Po prostu wygeneruj go w ogóle, czy użyj go w bardziej formalnym sensie wykonywania konwersji lwartości na rwartość?
Niezależnie od tego zdecydowanie nie można go przekonwertować na wartość r (§4.1 / 1):
Jeśli obiekt, do którego odnosi się lwartość, nie jest obiektem typu T i nie jest obiektem typu wywodzącego się z T lub jeśli obiekt nie jest zainicjowany, program, który wymaga tej konwersji, ma niezdefiniowane zachowanie.
Tutaj jest to zdecydowanie niezdefiniowane zachowanie.
Niejednoznaczność wynika z tego, czy jest to niezdefiniowane zachowanie, aby uszanować, ale nie używa wartości z nieprawidłowego wskaźnika (to znaczy, pobiera lwartość, ale nie konwertuje jej na rwartość). Jeśli nie, to int *i = 0; *i; &(*i);
jest dobrze zdefiniowane. To jest aktywny problem .
Mamy więc ścisły widok „wyłuskiwanie wskaźnika zerowego, uzyskanie niezdefiniowanego zachowania” i słaby widok „użyj wyłuskanego wskaźnika zerowego, uzyskaj niezdefiniowane zachowanie”.
Teraz rozważymy pytanie.
Tak, (a)
skutkuje niezdefiniowanym zachowaniem. W rzeczywistości, jeśli this
jest null, to niezależnie od zawartości funkcji wynik jest niezdefiniowany.
Wynika to z §5.2.5 / 3:
Jeśli E1
ma typ „wskaźnik do klasy X”, wówczas wyrażenie E1->E2
jest konwertowane na równoważną formę(*(E1)).E2;
*(E1)
spowoduje niezdefiniowane zachowanie ze ścisłą interpretacją i .E2
przekształci je na wartość r, co spowoduje niezdefiniowane zachowanie w przypadku słabej interpretacji.
Wynika z tego również, że jest to niezdefiniowane zachowanie bezpośrednio z (§9.3.1 / 1):
Jeśli niestatyczna funkcja składowa klasy X jest wywoływana dla obiektu, który nie jest typu X lub typu pochodnego od X, zachowanie jest niezdefiniowane.
W przypadku funkcji statycznych różnica polega na interpretacji ścisłej i słabej. Ściśle mówiąc, jest niezdefiniowany:
Do statycznego elementu członkowskiego można się odwoływać przy użyciu składni dostępu do elementu członkowskiego klasy, w którym to przypadku obliczane jest wyrażenie obiektu.
Oznacza to, że jest oceniany tak, jakby był niestatyczny i po raz kolejny wyłuskujemy wskaźnik zerowy z (*(E1)).E2
.
Jednak ponieważ E1
nie jest używany w statycznym wywołaniu funkcji składowej, jeśli używamy słabej interpretacji, wywołanie jest dobrze zdefiniowane. *(E1)
daje lwartość, funkcja statyczna jest rozwiązywana,*(E1)
odrzucana i wywoływana. Nie ma konwersji lwartości na rwartość, więc nie ma niezdefiniowanego zachowania.
W C ++ 0x, od n3126, niejednoznaczność pozostaje. Na razie bądź bezpieczny: stosuj ścisłą interpretację.