Понятие " Nil` в C++

вы помните из своих лекций по алгоритмам, что очень удобно иметь концепцию Nil, которому можно назначить или сравнить с чем угодно. (Кстати, я никогда не был студентом по информатике.) В Python мы можем использовать None; в Scala есть Nothing (который является подобъектом всего, если я правильно его понимаю). Но мой вопрос в том, как мы можем иметь Nil в C++? Ниже приведены мои мысли.

мы могли бы определить одноэлементный объект, используя the Синглтон Шаблон Проектирования

или мы могли бы определить глобальный или статический.

моя проблема в том, что в любом из этих случаев я не могу придумать способ назначить любую переменную любого типа Nil, или возможность сравнить любой объект любого типа с Nil. В Python None полезно, потому что Python динамически типизирован; Scala Nothing (не путать с Scala Nil, Что означает пустой список) решает элегантно, потому что Nothing является подобъектом всего. Так что есть элегантный способ иметь Nil в C++?

6 ответов


нет, нет такой вещи, как универсальный nil значение в C++. Дословно, типы C++ не связаны и не имеют общих значений.

вы можете достичь некоторые виде разделяемых ценностей с помощью наследования, но вы должны сделать это явно, и это может быть сделано только для пользовательских типов.


есть два связанных понятия, которые вы описываете как Nil: the тип блока и параметр типа.

Тип Блока

это NoneType в Python и nullptr_t находится в C++, это просто специальный тип, который имеет одно значение, которое передает это конкретное поведение. Поскольку Python динамически типизирован,любой


C++ следует принципу, что вы не платите за то, что не используете. Например, если вы хотите, чтобы 32-разрядное целое число хранило полный диапазон 32-разрядных значений и дополнительный флаг о том, равен ли он нулю, это займет более 32 бит хранения. Конечно, вы можете создать умные классы для представления этого поведения, но он недоступен "из коробки".


вы не можете иметь это в C++ без необходимости полагаться на контейнер, который инкапсулирует свой тип (например,boost::optional).

действительно, есть комбинация причин, так как C++ - это:

  • статически типизированный язык: как только вы определяете переменную с типом, она прилипает к этому типу (Что означает, что вы не можете назначить значение None что не относится к тому, что может представлять каждый тип, что вы могли бы сделать в других динамически типизированных языках, таких как Python)
  • язык со встроенными типами[1] и без общего базового объекта для его типов: вы не можете иметь семантику None плюс "все значения, которые этот тип может представлять в противном случае" в общем объекте для всех ваших типов.

[1] которые не похожи на встроенные типы Java, которые все имеют соответствующий класс, наследующий от java.lang.Object.


в большинстве этих языков переменные реализуются не как имена объектов, а как дескрипторы объектов.

такие ручки можно установить на "ничего не держать" с очень маленькими дополнительными накладными расходами. Это аналогично указателю на C++, где nullptr состояние соответствует "указывая ни на что".

часто такие языки делают полный сбор мусора. Как сбор мусора, так и принудительная косвенная ссылка на данные имеют значительные хиты производительности в практика, и они ограничивают какие операции можно сделать.

когда вы используете переменную в таких языках, вы должны сначала "следовать указателю", чтобы добраться до реального объекта.

в C++ имя переменной обычно ссылается на фактический объект, о котором идет речь, а не на ссылку на него. Когда вы используете переменную в C++, вы получаете к ней прямой доступ. Дополнительное состояние (соответствующее "ничего") потребует дополнительных накладных расходов в каждой переменной, и один из принципов C++ заключается в том, что вы не платите за то, что используете.

есть способы сделать тип данных "nullable" в C++. Они отличаются от использования необработанных указателей, использования интеллектуальных указателей или использования чего-то вроде std::experimantal::optional. Все они имеют состояние "там ничего нет", обычно обнаруживаемое путем взятия объекта и оценки его в bool контексте. Для доступа к фактическим данным (если они существуют) вы используете unary * или ->.


В C++11 есть значение nullptr,которое может быть присвоено любому типу указателя.

Если это не тип указателя, он должен быть там. Для конкретных значений вы можете определить специальные значения "я-не-существую", но универсального решения нет.

если он не может существовать, ваш выбор:

  • укажите на него и используйте nullptr укажите несуществующее значение или
  • сохранить отдельный флаг, чтобы указать на существование или несуществование (что boost::optional делает для вас), или
  • определите специальное значение для этого конкретного использования, чтобы представить несуществующее значение (обратите внимание, что это не всегда возможно.)