Entrambi (a)e (b)comportano un comportamento indefinito. È sempre un comportamento indefinito chiamare una funzione membro tramite un puntatore nullo. Se la funzione è statica, è anche tecnicamente indefinita, ma c'è qualche controversia.
La prima cosa da capire è perché è un comportamento indefinito dereferenziare un puntatore nullo. In C ++ 03, c'è effettivamente un po 'di ambiguità qui.
Sebbene "dereferenziare un puntatore nullo si traduca in un comportamento indefinito" sia menzionato nelle note sia in §1.9 / 4 che in §8.3.2 / 4, non viene mai dichiarato esplicitamente. (Le note non sono normative.)
Tuttavia, si può provare a dedurlo dal §3.10 / 2:
Un lvalue si riferisce a un oggetto o una funzione.
Quando si dereferenzia, il risultato è un lvalue. Un puntatore nullo non si riferisce a un oggetto, quindi quando usiamo lvalue abbiamo un comportamento indefinito. Il problema è che la frase precedente non viene mai pronunciata, quindi cosa significa "usare" lvalue? Basta anche solo generarlo o usarlo nel senso più formale di eseguire una conversione da valore a valore?
Indipendentemente da ciò, sicuramente non può essere convertito in un rvalue (§4.1 / 1):
Se l'oggetto a cui si riferisce lvalue non è un oggetto di tipo T e non è un oggetto di un tipo derivato da T, o se l'oggetto non è inizializzato, un programma che necessita di questa conversione ha un comportamento indefinito.
Qui è un comportamento decisamente indefinito.
L'ambiguità deriva dal fatto che sia o meno un comportamento indefinito da deferire ma non utilizzare il valore da un puntatore non valido (ovvero, ottenere un lvalue ma non convertirlo in un rvalue). In caso contrario, int *i = 0; *i; &(*i);è ben definito. Questo è un problema attivo .
Quindi abbiamo una vista rigorosa "dereferenziare un puntatore nullo, ottenere un comportamento non definito" e una vista debole "utilizzare un puntatore nullo dereferenziato, ottenere un comportamento indefinito".
Ora consideriamo la domanda.
Sì, si (a)traduce in un comportamento indefinito. Infatti, se thisè nullo, indipendentemente dal contenuto della funzione il risultato è indefinito.
Ciò segue dal §5.2.5 / 3:
Se E1ha il tipo "puntatore alla classe X", l'espressione E1->E2viene convertita nella forma equivalente(*(E1)).E2;
*(E1)si tradurrà in un comportamento indefinito con un'interpretazione rigorosa e lo .E2converte in un rvalue, rendendolo un comportamento indefinito per l'interpretazione debole.
Ne consegue anche che si tratta di un comportamento indefinito direttamente da (§9.3.1 / 1):
Se una funzione membro non statica di una classe X viene chiamata per un oggetto che non è di tipo X o di un tipo derivato da X, il comportamento è indefinito.
Con le funzioni statiche, l'interpretazione rigorosa rispetto a quella debole fa la differenza. A rigor di termini, è indefinito:
È possibile fare riferimento a un membro statico utilizzando la sintassi di accesso ai membri della classe, nel qual caso viene valutata l'espressione dell'oggetto.
Cioè, viene valutato come se non fosse statico e dereferenziamo ancora una volta un puntatore nullo con (*(E1)).E2.
Tuttavia, poiché E1non viene utilizzato in una chiamata statica di funzione membro, se usiamo l'interpretazione debole la chiamata è ben definita. *(E1)restituisce un lvalue, la funzione statica viene risolta, *(E1)viene scartata e la funzione viene chiamata. Non esiste una conversione da lvalue a rvalue, quindi non esiste un comportamento indefinito.
In C ++ 0x, a partire da n3126, l'ambiguità rimane. Per ora, stai al sicuro: usa l'interpretazione rigorosa.