Понятие " 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
делает для вас), или - определите специальное значение для этого конкретного использования, чтобы представить несуществующее значение (обратите внимание, что это не всегда возможно.)