C++: nullptr

In C and C++, the preprocessor definition NULL is/was used to explicitly indicate that a pointer is not pointing anywhere right now.

The problem with NULL is that underneath it is just a plain 0, something like this:

#define NULL 0

The following code excerpt shows how this can turn into a problem:

#include <iostream>
 
void m(int x)
{
    std::cout << x << endl;
}
 
void m(const char* x)
{
    std::cout << (x == NULL ? "NULL" : x) << std::endl;
}
 
int main()
{
  m(12);
  m(NULL);
  return 0;
}

When that code is compiled, the following error is displayed:

test.cpp: In function 'int main()':
test.cpp:19:9: error: call of overloaded 'm(NULL)' is ambiguous
test.cpp:19:9: note: candidates are:
test.cpp:5:6: note: void m(int)
test.cpp:10:6: note: void m(const char*)

Why does the compiler consider the m(NULL) ambiguous?

Because it cannot decide if NULL is actually a pointer (because it is a 0) or an integer (because NULL is… a 0).

C++11 introduces the literal nullptr of type std::nullptr_t; a literal that unambiguously represents a pointer pointing nowhere, eliminating the concept of being a pointer pointing to memory address 0.

It has these advantages:

  • It is a strongly typed null pointer, like Java or C# null.
  • No ambiguity when trying to pass a null pointer to something accepting, say, an int.
  • It is a literal and a keyword, so it cannot be defined or used as a symbolic name.
  • It cannot be casted automatically to a numeric type, as NULL can.

So, if the example above is replaced with this construction, there is this code:

int main()
{
  m(12);
  m(nullptr);
  return 0;
}

The code will compile and execute with no problems.

Since the type of nullptr is std::nullptr_t, which is a native type, programmers can also write a specific overload when the nullptr literal is passed as a parameter, as in the following example:

void m(int x)
{
    std::cout << x << std::endl;
}
 
void m(const char* x)
{
    std::cout << x << std::endl;
}
 
void m(nullptr_t)
{
    std::cout << "nullptr!" << std::endl;
}
 
int main()
{
  m(12);
  m(nullptr);
  return 0;
}

C++: Range-based for loop

Before C++11, when programmers wanted to display the elements stored in a vector, they had to write something like this:

template <typename T>
void show(const T& x)
{
    for (typename T::const_iterator i = x.begin(); i != x.end(); ++i)
        std::cout << *i << std::endl;
}

That method will be useful to show any collection that has a begin() and an end() and an iterator with an operator++(int), an operator!=() and an operator*() properly implemented.

C++11 and later versions ship with a very useful range-based for loop that makes iterations easier to write and read. This new for loop works also with plain-old arrays.

So, the code above can be rewritten like this in modern C++:

template <typename T>
void show(const T& x)
{
    for (auto& i : x)
        std::cout << i << std::endl;
}

As shown, the syntax is very similar to Java’s “enhanced-for” loop, and the resulting code is much easier to write and understand compared to the old version.

In the next example, you can see how this for loop can be used with several containers and arrays:

#include <vector>
#include <array>
#include <iostream>
  
template <typename T>
void show(const T& t)
{
    for (auto& i : t)
        std::cout << i << std::endl;
}
 
int main()
{
    int ints[] = { 10, 20, 30, 40, 50 };
    show(ints);
 
    std::cout << "*****" << std::endl;
 
    std::vector<std::string> s = { 
            "Monday", "Tuesday",
            "Wednesday", "Thursday",
            "Friday", "Saturday", "Sunday"
    };
 
    show(s);
     
    std::cout << "*****" << std::endl;
     
    std::array<string, 12> m = {
            "January", "February",
            "March", "April", "May",
            "June", "July", "August",
            "September", "October",
            "November", "December"
    };
 
    show(m);                            
    std::cout << "*****" << std::endl;
 
    return 0;   
}

Custom classes can also be iterated using the new for loop if the required methods are implemented, as in this example:

#include <vector>
#include <array>
#include <iostream>
 
template <typename T>
void show(const T& t)
{
    for (auto& i : t)
        std::cout << i << std::endl;
}
 
 
class RangeIterator
{
    private:
        int _index;
         
    public:
        explicit RangeIterator(int index) : _index(index) { }
         
        bool operator!=(const RangeIterator& x) const
        {
            return _index != x._index;
        }
         
        const int& operator*() const
        {
            return _index;
        }
         
        int& operator++()
        {
            return ++_index;
        }
};
 
template <int N, int M>
class Range
{
    public:
        using const_iterator = const RangeIterator;
         
        const_iterator begin() const
        {
            return const_iterator{N};
        }
         
        const_iterator end() const
        {
            return const_iterator{M};
        }
};
 
int main()
{
    Range<10, 20> r;
    show(r);
 
    return 0;   
}

Programmers can even iterate over a range of numbers (like the classic for loop) in this very elegant way:

int main()
{
    for (auto i : Range<10, 20>{})
    {
        std::cout << i << std::endl;
    }
}

The class to be iterated needs to have begin() and end() methods that return an iterator.

An iterator is basically a class that implements operator++(), operator!=(), and operator*() to access the next element, verify if there are more elements, and return the current element, respectively. Its semantics mimic pointer arithmetic to behave similarly to how a plain old array can be iterated.