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 E1
ha il tipo "puntatore alla classe X", l'espressione E1->E2
viene convertita nella forma equivalente(*(E1)).E2;
*(E1)
si tradurrà in un comportamento indefinito con un'interpretazione rigorosa e lo .E2
converte 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é E1
non 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.