TGViewer
Грокаем C++ Грокаем C++ @grokaemcpp · 9.35K subscribers
Post #422 5.34K
​​nullptr
#новичкам

Вероятно, каждый, кто писал код на C++03, имел удовольствие использовать NULL и постоянно ударяться мизинцем ноги об этот острый уголок тумбочки. Дело в том, что NULL использовался, как обозначение нулевого указателя, который никуда не указывает. Но если он для этого использовался - это не значит, что он таковым и являлся. Это макрос, который мог быть определен как 0 aka int zero или 0L aka zero long int, но всегда это вариация интегрального нуля. И уже эти чиселки могли быть приведены к типу указателя.

Вот в этом-то и вся проблема. NULL очень явно хочет себя видеть в виде указателя, но по факту в зеркале видит число. Допустим, у нас есть 2 перегрузки одной функции: одна для инта, вторая для указателя:

class Spell { ... };

void castSpell(Spell * theSpell);
void castSpell(int spellID);

int main() {
castSpell(NULL);
}


Намерения ясны: мы хотим вызвать перегрузку для указателя. Но это гарантировано не произойдет! В произойдет один из двух сценариев: если NULL определен как 0, то просто без объявления войны в 4 часа утра вызовется вторая перегрузка. Если как 0L, то компилятор поругается на неоднозначный вызов: 0L может быть одинаково хорошо сконвертирован и в инт, и в указатель.

Проблему можно решить енамами и передавать для нулевого spellID что-то типа NoSpell. Но надо опять городить огород. Почему все не работает из коробки?!

С приходом С++11 начало работать из коробки. Надо только забыть про NULL и использовать nullptr.

Ключевое слово nullptr обозначает литерал указателя. Это prvalue типа std::nullptr_t. И nullptr неявно приводится к нулевому значению указателя для любого типа указателя. Это объект отдельного типа, который теперь к простому инту не приводится.

Поэтому сейчас этот код отработает как надо:

class Spell { ... };

void castSpell(Spell* theSpell);
void castSpell(int spellID);

int main() {
castSpell(nullptr);
}



Так как nullptr - значение конкретного типа std::nullptr_t, то мы может принимать в функции непосредственно этот тип, а не общий тип указателя. Такая штука используется, например, в реализации std::function, конструктор которого имеет перегрузку для std::nullptr_t и делает тоже самое, что и конструктор без аргументов.

/**
* @brief Default construct creates an empty function call wrapper.
* @post !(bool)this
/
function() noexcept
: _Function_base() { }

/
* @brief Creates an empty function call wrapper.
* @post @c !(bool)*this
/
function(nullptr_t) noexcept
: _Function_base() { }


По той же причине nullptr даже при возврате через функцию может быть приведен к типу указателя. А вот обычные null pointer константны не могут похвастаться таким свойством. Они могут приводиться к указателям только в виде литералов.

template<class T>
constexpr T clone(const T& t)
{
return t;
}
 
void g(int *)
{
std::cout << "Function g called\n";
}
 
int main()
{
g(nullptr); // Fine
g(NULL) // Fine
g(0); // Fine
 
g(clone(nullptr)); // Fine
// g(clone(NULL)); // ERROR: non-literal zero cannot be a null pointer constant
// g(clone(0)); // ERROR: non-literal zero cannot be a null pointer constant
}


clone(nullptr) вернет тот же nullptr и все будет работать гладко. А для 0 и NULL функция вернет просто int, который сам по себе неявно не конвертится в указатель.

Думаю, что вы все и так пользуете nullptr, но этот пост обязан быть на канале.

Как говорит одна древняя мудрость: "Use nullptr instead of NULL, 0 or any other null pointer constant, wherever you need a generic null pointer."

Be a separate subject. Stay cool.

#cppcore #cpp11
  • 👍 30
  • ❤ 8
  • 🔥 6
  • 🤔 2
More from @grokaemcpp
  1. Oct 14, 2026​​Что внутри std::condition_variable? #опытным Более менее все знакомы с примитивом синхро…
  2. Oct 12, 2026​​WAT #новичкам Как вы думаете, результат выражения: bool math = -1 < 1U; будет правдой ил…
  3. Oct 8, 2026​​Strict weak ordering #опытным На первый взгляд, всё выглядит рабочим: мы создаём 40 зака…
  4. Oct 7, 2026​​Где-то баг... #опытным Вот вам код: struct Order { int price; int id; }; int main() { st…
  5. Oct 5, 2026Откуда spurious wakeup на кондваре? #опытным У кондваров есть метод std::condition_variabl…
  6. Oct 1, 2026​​Stacktrace. Tips #опытным Чтобы полноценно работать со стандартными трейсами, нужно знат…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →