Ambos (a)
y (b)
dan como resultado un comportamiento indefinido. Siempre es un comportamiento indefinido llamar a una función miembro a través de un puntero nulo. Si la función es estática, técnicamente tampoco está definida, pero hay alguna disputa.
Lo primero que hay que entender es por qué es un comportamiento indefinido desreferenciar un puntero nulo. En C ++ 03, en realidad hay un poco de ambigüedad aquí.
Aunque "desreferenciar un puntero nulo da como resultado un comportamiento indefinido" se menciona en notas tanto en §1.9 / 4 como en §8.3.2 / 4, nunca se indica explícitamente. (Las notas no son normativas).
Sin embargo, se puede intentar deducirlo de §3.10 / 2:
Un lvalue se refiere a un objeto o función.
Al eliminar la referencia, el resultado es un valor l. Un puntero nulo no se refiere a un objeto, por lo tanto, cuando usamos lvalue tenemos un comportamiento indefinido. El problema es que la oración anterior nunca se dice, entonces, ¿qué significa "usar" el valor l? ¿Solo generarlo o usarlo en el sentido más formal de realizar conversión de valor a valor?
Independientemente, definitivamente no se puede convertir a un rvalue (§4.1 / 1):
Si el objeto al que se refiere lvalue no es un objeto de tipo T y no es un objeto de un tipo derivado de T, o si el objeto no está inicializado, un programa que necesita esta conversión tiene un comportamiento indefinido.
Aquí es definitivamente un comportamiento indefinido.
La ambigüedad proviene de si es o no un comportamiento indefinido para deferencia pero no usar el valor de un puntero no válido (es decir, obtener un lvalue pero no convertirlo en un rvalue). Si no, entonces int *i = 0; *i; &(*i);
está bien definido. Este es un tema activo .
Así que tenemos una vista estricta de "desreferenciar un puntero nulo, obtener un comportamiento indefinido" y una vista débil de "usar un puntero nulo desreferenciado, obtener un comportamiento indefinido".
Ahora consideramos la pregunta.
Sí, (a)
da como resultado un comportamiento indefinido. De hecho, si this
es nulo, independientemente del contenido de la función, el resultado no está definido.
Esto se deriva de §5.2.5 / 3:
Si E1
tiene el tipo "puntero a la clase X", la expresión E1->E2
se convierte a la forma equivalente(*(E1)).E2;
*(E1)
dará como resultado un comportamiento indefinido con una interpretación estricta y lo .E2
convierte en un valor r, lo que lo convierte en un comportamiento indefinido para la interpretación débil.
También se deduce que es un comportamiento indefinido directamente de (§9.3.1 / 1):
Si se llama a una función miembro no estática de una clase X para un objeto que no es de tipo X, o de un tipo derivado de X, el comportamiento no está definido.
Con funciones estáticas, la interpretación estricta frente a la débil marca la diferencia. Estrictamente hablando, no está definido:
Se puede hacer referencia a un miembro estático utilizando la sintaxis de acceso de miembro de clase, en cuyo caso se evalúa la expresión de objeto.
Es decir, se evalúa como si no fuera estático y una vez más desreferenciamos un puntero nulo con (*(E1)).E2
.
Sin embargo, debido a E1
que no se usa en una llamada de función miembro estática, si usamos la interpretación débil, la llamada está bien definida. *(E1)
da como resultado un valor l, la función estática se resuelve, *(E1)
se descarta y se llama a la función. No hay conversión de lvalue a rvalue, por lo que no hay un comportamiento indefinido.
En C ++ 0x, a partir de n3126, la ambigüedad permanece. Por ahora, tenga cuidado: use la interpretación estricta.