Down with pointers
Some years ago, there was an Aprils fool post from various other C++ bloggers that C++ has deprecated pointers (for example, Fluent C++ - C++ Will No Longer Have Pointers. Well, as C++ nearly never deprecates anything, that alone was unbelievable. However, do we still need pointers? That's the question I want to cover in today's post.
What pointers say
In C++, pointers signal that a parameter may have a value, or not. Whenever a function receives a pointer, we should write a check in the body whether the parameter is a
nullptr. Sadly, I already saw many cases where this check was omitted. All documentation and comments like a valid non-null object is required do not help and don't make that check obsolete.
I also have seen cases where the
nullptr check on a function parameter was omitted because it was hard to decide what to do in the circumstance of a
nullptr. Say a function that returns
void but has received a
The other interesting part is that this check comes with costs. The compiler, at least from what I have seen, is not able to optimize such a check away, even in a small program. See below for more details.
Use references instead of pointers
This necessity for a check, and the endless comments, go away once we switch to a reference. In contrast to a pointer, a reference expresses that a valid object is required at this point.
A simple approach is to still receive pointers on API boundaries if, for example, you can't change the API. But then, first thing in that function, do the
nullptr-check, return if the pointer is
null. If it is valid, dereference the pointer and store it in a reference.
1 2 3 4 5 6 7 8
That way, we can at least keep the internal API and code clean. Maybe with the next release, we will get a chance to clean up the public API as well.
Wait, I need a maybe parameter
Okay, then let's change all pointers to references. But what if I need such a maybe parameter? Hm, with maybe you mean optional? Right! For that case, C++17 brings us
std::optional. So please stop abusing pointers when you want to express that the parameter is optional. No need to convert an
int into an
int* just to have the value
nullptr available for comparison.
1 2 3 4 5 6
std::optional is so much better than a pointer. With functions like
get_value_or, it spares us writing an annoying
if which adjusts the value to the stored one or the default.
Okay, but what is with, say, an array? Say we want to pass an array to a function there, we can't use references, except if we make it a template. Oh, and please don't say
std::array because I want this function to be callable with various different array sizes. There I still need a pointer! Got you!
1 2 3 4 5 6 7 8 9 10 11
string_view to the rescue
Well, no. At least we don't need a pointer in the API of the function. C++20 brings us
std::span for cases where we want to pass an array or a contiguous container (in this example here, we might also use
std::string_view from C++17). The advantage of
std::span is that it carries the number of elements of the data. So no additional size parameter and way fewer
1 2 3 4 5 6 7 8 9 10 11
I think we are at a stage where we can say that there is no need for a pointer for a top-level API anymore. With helper types like
std::span, we can do much better. And yes, pointers are still a thing in C++ and should be. For example,
std::span takes and returns a pointer.
Why do I care that much?
Well, I like clean and expressive APIs. What I also like is efficient code. Have a look at the following example on Compiler Explorer and see for yourself godbolt.org/z/T6qq5q3Tb. You see a full program, including
main. The function
Fun that takes a pointer and checks for
nullptr consumes 7 instructions with
-O3. The version without a check, as well as the reference version, only consumes 3 instructions. This is for a case where the compiler sees the entire program! The interesting part is
Opt. Here I use a
std::optional together with
get_value_or. So essentially, the value is checked. However, both Clang and GCC manage to compile that function to 6 lines of assembly. Not bad, right? Okay, the library part is missing here, so we get some additional costs for the
Do we still need pointers?
Well, I hope I showed you that we at least need them less frequently than we used to. Pointers are still an essential part of C++, but we can use better data types in a lot of places.