І те (a)
й (b)
інше призводить до невизначеної поведінки. Завжди невизначеною поведінкою викликати функцію члена через нульовий покажчик. Якщо функція статична, вона також технічно не визначена, але є певна суперечка.
Перше, що потрібно зрозуміти, чому це невизначена поведінка, щоб знецінити нульовий покажчик. У C ++ 03 тут насправді є трохи неоднозначності.
Хоча "перенаправлення нульового вказівника призводить до невизначеної поведінки" згадується в примітках як у §1.9 / 4, так і в § 8.3.2 / 4, це ніколи прямо не зазначено. (Примітки ненормативні.)
Однак можна спробувати вивести це з § 3.10 / 2:
Lvalue означає предмет або функцію.
При перенаправлення результату є значення. Нульовий вказівник не посилається на об'єкт, тому при використанні lvalue ми маємо невизначену поведінку. Проблема полягає в тому, що попереднє речення ніколи не висловлюється, тож що означає "використовувати" lvalue? Просто навіть генерувати його взагалі або використовувати його в більш формальному сенсі для здійснення конверсії lvalue-to-rvalue?
Незважаючи на це, його напевно не можна перетворити на рецензію (§4.1 / 1):
Якщо об'єкт, на який посилається значення lvalue, не є об'єктом типу T і не є об'єктом типу, похідним від T, або якщо об'єкт неініціалізований, програма, яка потребує цього перетворення, має невизначене поведінку.
Тут напевно не визначена поведінка.
Неоднозначність походить від того, чи це не визначена поведінка на затримку, але не використовувати значення з недійсного вказівника (тобто, отримати lvalue, але не перетворити його в rvalue). Якщо ні, то int *i = 0; *i; &(*i);
це чітко визначено. Це активне питання .
Таким чином, у нас є суворий перегляд "dereference null pointer, view undefined поведінка" і слабкий "use a dereferenced null pointer, get undefined поведінка" view.
Тепер ми розглянемо питання.
Так, це (a)
призводить до невизначеної поведінки. Насправді, якщо this
нуль, то незалежно від змісту функції, результат не визначений.
Це випливає з § 5.2.2 / 3:
Якщо E1
має тип "покажчик на клас X", то вираз E1->E2
перетворюється в еквівалентну форму(*(E1)).E2;
*(E1)
призведе до невизначеної поведінки із суворою інтерпретацією та .E2
перетворить її на реальне значення, зробивши її невизначеною поведінкою для слабкої інтерпретації.
Звідси випливає, що це невизначена поведінка безпосередньо з (§ 9.3.1 / 1):
Якщо нестатична функція члена класу X викликається для об'єкта, який не типу X, або типу, похідного від X, поведінка не визначена.
При статичних функціях сувора та слабка інтерпретація має значення. Строго кажучи, це не визначено:
Статичний член може бути посилається на використання синтаксису доступу члена класу, в цьому випадку оцінюється об'єкт-вираз.
Тобто, вона оцінюється так, ніби вона нестатична, і ми знову перенаправляємо нульовий покажчик (*(E1)).E2
.
Однак, оскільки E1
не використовується у статичному виклику функції члена, якщо ми використовуємо слабку інтерпретацію, виклик є чітко визначеним. *(E1)
приводить до значення, значення статичної функції вирішується, *(E1)
відкидається, а функція викликається. Немає конверсії lvalue to rvalue, тому немає визначеної поведінки.
У C ++ 0x станом на n3126 неоднозначність залишається. Поки що будьте безпечні: використовуйте суворе тлумачення.