Ambos (a)
e (b)
resultam em comportamento indefinido. É sempre um comportamento indefinido chamar uma função de membro por meio de um ponteiro nulo. Se a função é estática, também é tecnicamente indefinida, mas há alguma disputa.
A primeira coisa a entender é por que é um comportamento indefinido desreferenciar um ponteiro nulo. No C ++ 03, há realmente um pouco de ambiguidade aqui.
Embora "desreferenciar um ponteiro nulo resulte em comportamento indefinido" seja mencionado nas notas nos §1.9 / 4 e §8.3.2 / 4, nunca é explicitamente declarado. (As notas não são normativas.)
No entanto, pode-se tentar deduzi-lo de §3.10 / 2:
Um valor l refere-se a um objeto ou função.
Ao cancelar a referência, o resultado é um valor l. Um ponteiro nulo não se refere a um objeto; portanto, quando usamos o lvalue, temos um comportamento indefinido. O problema é que a sentença anterior nunca é declarada, então o que significa "usar" o lvalue? Apenas gerá-lo ou usá-lo no sentido mais formal de realizar conversão de valor em valor?
Independentemente disso, definitivamente não pode ser convertido em um rvalor (§4.1 / 1):
Se o objeto ao qual o lvalue se refere não for um objeto do tipo T e não for um objeto de um tipo derivado de T ou se o objeto não for inicializado, um programa que necessite dessa conversão terá um comportamento indefinido.
Aqui é definitivamente um comportamento indefinido.
A ambiguidade vem do fato de ser ou não um comportamento indefinido para deferência, mas não usar o valor de um ponteiro inválido (ou seja, obter um lvalue, mas não convertê-lo em um rvalue). Caso contrário, int *i = 0; *i; &(*i);
está bem definido. Este é um problema ativo .
Portanto, temos uma exibição estrita de "desreferenciar um ponteiro nulo, obter comportamento indefinido" e uma exibição fraca de "usar um ponteiro nulo não referenciado, obter comportamento indefinido".
Agora consideramos a questão.
Sim, (a)
resulta em comportamento indefinido. De fato, se this
for nulo, independentemente do conteúdo da função, o resultado será indefinido.
Isto segue do §5.2.5 / 3:
Se E1
tiver o tipo "ponteiro para a classe X", a expressão E1->E2
será convertida para a forma equivalente(*(E1)).E2;
*(E1)
resultará em comportamento indefinido com uma interpretação estrita e o .E2
converterá em um rvalor, tornando-o um comportamento indefinido para a interpretação fraca.
Segue-se também que é um comportamento indefinido diretamente de (§9.3.1 / 1):
Se uma função de membro não estática de uma classe X for chamada para um objeto que não seja do tipo X ou de um tipo derivado de X, o comportamento será indefinido.
Com funções estáticas, a interpretação estrita versus fraca faz a diferença. A rigor, é indefinido:
Um membro estático pode ser referido usando a sintaxe de acesso de membro da classe; nesse caso, a expressão do objeto é avaliada.
Ou seja, é avaliado como se não fosse estático e, mais uma vez, desreferenciamos um ponteiro nulo (*(E1)).E2
.
No entanto, como E1
não é usada em uma chamada de função de membro estática, se usarmos a interpretação fraca, a chamada será bem definida. *(E1)
resulta em um lvalue, a função estática é resolvida,*(E1)
é descartada e a função é chamada. Não há conversão de valor em valor, portanto, não há comportamento indefinido.
No C ++ 0x, a partir do n3126, a ambiguidade permanece. Por enquanto, esteja seguro: use a interpretação estrita.