И (a)
, и (b)
приводят к неопределенному поведению.Это всегда неопределенное поведение - вызывать функцию-член через нулевой указатель.Если функция статическая, она также технически не определена, но есть некоторый спор.
Первое, что нужно понять, - это то, почему неопределенное поведение - разыменовать нулевой указатель.В C ++ 03 здесь есть некоторая двусмысленность.
Хотя «разыменование нулевого указателя приводит к неопределенному поведению» упоминается в примечаниях как в §1.9 / 4, так и в §8.3.2 / 4, это никогда не указывалось явно.(Примечания не являются нормативными.)
Однако можно попытаться вывести его из §3.10 / 2:
Значение l относится к объекту или функции.
При разыменовании, результатом является lvalue.Нулевой указатель не относится к объекту, поэтому при использовании lvalue мы имеем неопределенное поведение.Проблема в том, что предыдущее предложение никогда не формулируется, так что значит «использовать» lvalue?Просто даже сгенерировать его вообще или использовать в более формальном смысле для выполнения преобразования lvalue в rvalue?
Несмотря на это, оно определенно не может быть преобразовано в rvalue (§4.1 / 1):
Если объект, на который ссылается lvalue, не является объектом типа T и не является объектом типа, производного от T, или если объект неинициализирован, программа, для которой требуется это преобразование, имеет неопределенное поведение.
Здесь это определенно неопределенное поведение.
Неоднозначность возникает из-за того, является ли неопределенное поведение отклонением , но не использованием значения из недопустимого указателя (то есть получить lvalue, но не преобразовывать его в rvalue).Если нет, то int *i = 0; *i; &(*i);
четко определено.Это активная проблема .
Таким образом, мы имеем строгое представление «разыменовать нулевой указатель, получить неопределенное поведение» и слабое представление «использовать разыменованный нулевой указатель, получить неопределенное поведение».
Теперь рассмотрим вопрос.
Да, (a)
приводит к неопределенному поведению.Фактически, если this
равно нулю, то независимо от содержимого функции результат не определен.
Это следует из §5.2.5 / 3:
Если E1
имеет тип «указатель на класс X», тогда выражение E1->E2
преобразуется в эквивалентную форму (*(E1)).E2;
*(E1)
приведет к неопределенному поведению сстрогое толкование, и .E2
преобразует его в значение r, что делает его неопределенным поведением для слабой интерпретации.
Из этого также следует, что это неопределенное поведение непосредственно из (§9.3.1 / 1):
Если нестатическая функция-член класса X вызывается для объекта, который не относится к типу X или к типу, производному от X, поведение не определено.
При статических функциях разница между строгой и слабой интерпретацией.Строго говоря, он не определен:
На статический член можно ссылаться с использованием синтаксиса доступа к члену класса, и в этом случае вычисляется выражение объекта.
Тото есть он оценивается так же, как если бы он был нестатичным, и мы снова разыменовываем нулевой указатель с помощью (*(E1)).E2
.
Однако, поскольку E1
не используется в статическом вызове функции-члена, еслииспользуйте слабую интерпретацию, вызов четко определен.*(E1)
приводит к lvalue, статическая функция разрешается, *(E1)
отбрасывается и вызывается функция.Преобразование из lvalue в rvalue отсутствует, поэтому не существует неопределенного поведения.
В C ++ 0x, начиная с n3126, неопределенность сохраняется.Пока будьте в безопасности: используйте строгую интерпретацию.