Почему разыменование nullptr приводит к ошибке?

Рейтинг: 2Ответов: 1Опубликовано: 20.05.2023

Почему не компилируется *nullptr? Ведь сказано, что он приводим к любому указательному типу. Как я понимаю, это означает operator T*, а значит должно быть неявное преобразование и должно компилироваться. Либо быть ошибка, что непонятно к какому типу приводить.

struct A {
    operator int() {return 42;}
} a;

int main() {
    +a;       // OK
    *nullptr; // CE
}

Ответы

▲ 3

Рассмотрим требования для операнда встроенного унарного *.

[expr.unary.op]/1:

The unary * operator performs indirection: the expression to which it is applied shall be a pointer to an object type, or a pointer to a function type and the result is an lvalue referring to the object or function to which the expression points. ...

Но тип выражения nullptr не является ни указателем на объект, ни указателем на функцию. На самом деле nullptr имеет свой отдельный фундаментельный тип, который не может быть выражен средствами языка (как int), не может быть классом или указателем.

[support.types.nullptr]/1:

The type nullptr_t is a synonym for the type of a nullptr expression, and it has the characteristics described in [basic.fundamental] and [conv.ptr]. ...

[cstddef.syn]:

namespace std {
  ...
  using nullptr_t = decltype(nullptr);
  ...
}

[basic.fundamental]/14:

A value of type std​::​nullptr_­t is a null pointer constant. ...

[basic.fundamental]/15:

The types described in this subclause are called fundamental types. ...

Хорошо, но ведь мы можем привести этот тип к указательному типу. Почему не происходит неявного преобразования? — Для того чтобы неявное преобразование вообще рассматривалось, необходимо чтобы существовал тип, к которому мы хотим привести.

[conv.general]/1:

...

A standard conversion sequence will be applied to an expression if necessary to convert it to a required destination type.

У нас же такого типа нет (и это не может быть тип void, т.к. это не объектный тип).


Ошибки вида "use of overloaded operator '*' is ambiguous" не может возникнуть, т.к. она подразумевает процесс overload resolution, который требует наличия хотя бы одного нефундаментального типа в операндах оператора. Другими словами, если все операнды оператора не являются классами/перечислениями, то overload resolution происходить не будет.


Если сделать преобразование к объектному типу явно, то целевой тип появится и всё будет работать:

int main() {
    // *((void*)nullptr); // CE, not an object type
    *((int*)nullptr);  // OK, btw no UB
}