Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

The concept of `Nil` in C++

Tags:

c++

null

You remember from your undergrad lectures in algorithms that it's very handy to have a concept of Nil, which anything can be assigned to or compared with. (By the way I never did an undergrad in computer science.) In Python we can use None; in Scala there is Nothing (which is a subobject of everything if I'm understanding it correctly). But my question is, how can we have Nil in C++? Below are my thoughts.

We could define a singleton object using the Singleton Design Pattern, but my current impression is that most of you would wince at the very thought of that.

Or we could define a global or a static.

My problem is that in either of these cases, I can't think of a way of being able to assign any variable of any type to Nil, or being able to compare any object of any type with Nil. Python's None is useful because Python is dynamically typed; Scala's Nothing (not to be confused with Scala Nil, which means empty list) solves this elegantly because Nothing is a subobject of everything. So is there an elegant way of having Nil in C++?

like image 208
Ray Avatar asked Apr 09 '15 18:04

Ray


People also ask

Is nil and NULL the same in C?

NULL is a void *, nil is an id, and Nil is a Class pointer, NULL is used for non-object pointer (like a C pointer) in Objective-C. Like nil , NULL got no value nor address (used to check if a struct is empty). Keep in mind these quotes: In Objective-C: nil is a pointer to a non-existent object.

Why do we use NULL in C?

A null pointer has a reserved value that is called a null pointer constant for indicating that the pointer does not point to any valid object or function. You can use null pointers in the following cases: Initialize pointers. Represent conditions such as the end of a list of unknown length.

Can a pointer be nil?

Nil Pointers All variables in Go have a zero value. This is true even for a pointer. If you declare a pointer to a type, but assign no value, the zero value will be nil .

WHAT IS null pointer in C with example?

A Null Pointer is a pointer that does not point to any memory location. It stores the base address of the segment. The null pointer basically stores the Null value while void is the type of the pointer. A null pointer is a special reserved value which is defined in a stddef header file.


3 Answers

No, there is no such thing as a universal nil value in C++. Verbatim, C++ types are unrelated and do not share common values.

You can achieve some form of shareable values by using inheritance, but you must do so explicitly, and it can only be done for user-defined types.

like image 194
Angew is no longer proud of SO Avatar answered Oct 21 '22 01:10

Angew is no longer proud of SO


There are two related concepts what you describe as Nil: the unit type and the option type.

Unit Type

This is what NoneType is in Python and nullptr_t is in C++, it's just a special type that has a single value that conveys this specific behavior. Since Python is dynamically typed, any object can be compared against None but in C++ we can only do this for pointers:

void foo(T* obj) {
    if (obj == nullptr) {
        // Nil
    }
}

This is semantically identical to python's:

def foo(obj):
    if foo is None:
        # Nil

Option Type

Python does not have (or need) such a feature, but all the ML family do. This is implementable in C++ via boost::optional. This is a type-safe encapsulation of the idea that a particular object can maybe have a value, or not. This idea is more expressive in the functional family than it is in C++ though:

def foo(obj: Option[T]) = obj match {
    case None => // "Nil" case
    case Some(v) => // Actual value case
}

Although easy enough to understand in C++ too, once you see it:

void foo(boost::optional<T> const& obj) {
    if (obj) {
        T const& value = *obj;
        // etc.
    }
    else {
        // Nil
    }
}

The advantage here is that the option type is a value type, and you can easily express a "value" of nothing (e.g. your optional<int*> can store nullptr as a value, as separate from "Nil"). Also this can work with any type, not just pointers - it's just that you have to pay for the added functionality. An optional<T> will be larger than a T and be more expensive to use (although only by a little, although that little could matter a lot).

A C++ Nil

We can combine those ideas together to actually make a Nil in C++, at least one that works with pointers and optionals.

struct Nil_t { };
constexpr Nil_t Nil;

// for pointers
template <typename T>
bool operator==(const T* t, Nil_t ) { return t == nullptr; }

template <typename T>
bool operator==(Nil_t, const T* t ) { return t == nullptr; }

// for optionals, rhs omitted for brevity
template <typename T>
bool operator==(const boost::optional<T>& t, Nil_t ) { return !t; }

(Note that we can even generalize this to anything that implements operator!

template <typename T>
bool operator==(const T& t, Nil_t ) { return !t; }

, but it would be better to limit ourselves to the clear-cut cases and I like the explicitness that pointers and optionals give you)

Thus:

int* i = 0;
std::cout << (i == Nil); // true
i = new int(42);
std::cout << (i == Nil); // false
like image 43
Barry Avatar answered Oct 21 '22 01:10

Barry


C++ follows the principle that you don't pay for what you don't use. For example, if you want a 32-bit integer to store the full range of 32-bit values and an additional flag about whether it is Nil, this will take more than 32-bits of storage. Certainly you can create clever classes to represent this behaviour, but it's not available "out of the box".

like image 3
Neil Kirk Avatar answered Oct 21 '22 01:10

Neil Kirk