Modern C++ Isn't Scary
Tags: cpp
Published: 2019-08-16 16:58:06

---
title: Modern C++ Isn't Scary
published: true
description: Four Months of Developing w/ Modern C++ Changed My Opinion on the Language 
tags: cplusplus
cover_image: https://thepracticaldev.s3.amazonaws.com/i/ekqwq8h8hn8nize4iw2q.jpg
---

C++ has a reputation for being an obtuse, antiquated language that is only for neckbeards and angry professors. Sure, it's fast, but why do you want to learn all of that nasty overhead just for a little more speed? And I love my package manager and tooling! Does C++ have anything modern?

This is exactly how I felt after every time I looked at C++ or worked on a C++ project since I graduated college. However, I've had to write a considerable amount of C++ recently and I have to say...

C++17 ain't your dad's C++.

# Less horrendous syntax

C++ is notorious for its horrendous syntax. However, starting with C++11 the syntax has gradually been made much more appealing. A lot of it involves the use of the keyword `auto`. You can now use range-based `for` loops like in Python and C# (technically `foreach`):

```c++
double arr[3] = { 1.0, 2.0, 3.0 };

for (auto& x : arr) {
    std::cout << x << std::endl;
}
```
The `&` after `auto` signifies that we don't want to make a copy of the item in the array (or `std::vector` or any other iterable collection).
Auto can be used to destructure `std::tuple`s and `std::pair`s, as well as `struct`s and arrays! If we use our array above, we can destructure the whole thing:

```c++
auto [a, b, c] = arr;
// auto [a, b] won't compile because our array is length 3
```

You can use it in `for` loops on `std::map`s as well:

```c++
for(const auto& [k,v] : dictionary) {
...
}
```

Destructuring tuples works the same way:

```c++
std::tuple<double, std::string> t{ 4.0, "GPA" };
auto [value, label] = t;
```
That tuple looks to be defined in standard, verbose C++. Well, they've got a fix for that too...

# Template Parameter Inference

If the type can be inferred in a template type, then there is no need to specify the types! I can rewrite the `std::tuple` definition above as:

```c++
std::tuple t{ 4.0, R"(GPA)" };
// or better yet
auto t = std::tuple{ 4.0, R"(GPA") };
```

The `R"(...)"` creates a string literal instead of a character array. This is considerably less bulky than previous iterations of the C++ language standard, and it makes type signatures a lot less irritating. You'll still have to fully qualify the type on function return types, but you can `auto` the pain away everywhere else.

The language standard committee has also added `std::tie` to integrate with pre-existing code as well, where you want to tie values as output of a function to variables that already exist. The language has really made it **MUCH** nicer than it used to be regarding functions that have multiple output values, and I'm quite satisfied with it.

# Literals

C++ has long had some literals for integer and floating point types (i.e., defining zero as an unsigned long as `0UL` or as a float as `0.0f`). In C++11/14, have expanded the scope of literals to include booleans, strings (both UTF-8 *and* UTF-16), units of time (`42ns` is the same as `std::chrono::nanoseconds(42)`), and a lot more! There is also the option for [user-defined literals](https://en.cppreference.com/w/cpp/language/user_literal), and the documentation is pretty solid. This has been one of the more exciting features for me personally!

# More platform independent libraries

Several things were classically OS-specific in C++; there were no overarching abstractions. Thankfully, in C++11 and beyond that has been remedied.

For example, if you wanted to sleep the current thread for 5 seconds, in grandpa's C++ you'd write:
```c++
#include <unistd.h>

...
void sleep_func(unsigned int milliseconds) {
    usleep(milliseconds * 1000);
}
...

unsigned int sleep_seconds = 5;
sleep_func(sleep_seconds * 1000);

```
and that would work for \*nix environments **only**. If you wanted to do sleep for some unit other than microseconds (the Unix implementation's unit of measure), you would have to do the conversion yourself. While not difficult, it's still prone to human error. If you wanted to do it on a specific thread... well, that's a whole different story. In C++11, you can use the `std::chrono` library, with a [variety of clocks](https://en.cppreference.com/w/cpp/header/chrono), as well as the `std::thread` library for thread-specific tasks.

```c++
#include <chrono>
#include <thread>

...
void sleep_func(std::chrono::duration duration) {
    std::this_thread::sleep_for(duration);
}
...

auto sleep_seconds = 5;
sleep_func(std::chrono::seconds(sleep_seconds));

```

There are several pre-defined durations in the `std::chrono` namespace also, so that it is incredibly clear to see how the units of your time span, and all the conversions are handled by the compiler. Less work for us!

They've also **finally** implemented a [filesystem](https://en.cppreference.com/w/cpp/filesystem/path) abstraction in C++17! It was experimental in C++14 but officially became a part of the language standard with the last release.

# Creating objects in template classes

In the "good ol' days" of C++, using template collections was **super annoying**. If you wanted to push something to the back of a queue, for instance, you would have to create the object and then pass a copy of that object into the queue.

```c++
std::vector<thing> things;

for (int i = 0; i < 50; i++) {
   thing t("foo", i);
   things.emplace_back(t);
}
```
A lot of template classes now have functions that would copy previously that have an `Args&&`-style implementation now so that a new object of that type can be created in place in the template class! That looks something like:

```c++
std::vector<thing> things;

for (int i = 0; i < 50; i++) {
    things.emplace_back("foo", i);
}
```

This saves some copying overhead and speeds things up as well, and discourages the use of pointers in collections (when appropriate).

# Better Pointers

Let's face it: dealing with raw pointers SUCKS. Allocating memory. Freeing memory. Creating a pointer in one scope and assuming ownership transfers to a new scope later. All of these cases require a lot of ceremony, can cause memory leaks, and require a lot of mental overhead to make sure you don't cause memory leaks. Thankfully, C++11 brought different pointer types developed in the Boost library into the language specification.

## Unique Pointers
`std::unique_ptr<T>` can only ever be referenced by one object. If ownership needs to be transferred to a different scope (but still only maintain one copy), std::move can be used to transfer ownership. This can be useful in factories, or any other time that you might want to create something and pass its ownership to another object. For example, you may want to create a stream of bytes coming off a `Socket` and pass the ownership of that data to a requesting object.

## Shared Pointers
`std::shared_ptr<T>` are officially one of my favorite things in C++. If some point needs to be referenced by multiple objects (like, say, a Singleton for a Websocket), in old school C++ you would've created a raw pointer to that object and destroy it on cleanup or after its last use... *hopefully*. Raw pointers are one of the single biggest contributors to memory leaks in C++. They probably rank up there with NULL pointers as a billion dollar mistake.

Thankfully, shared pointers are now a thing and widely accepted in the C++ community. When copied (and they should **always** be copied, not passed by reference or pointer), they increment an internal reference count. When these copies are destroyed, the reference count is decremented. When the reference count reaches zero, then the object is destroyed and the memory is freed up. No more manual memory management hassle! Yay! You can still shoot your foot off with shared pointers if you don't strictly copy them, but there's a better safety mechanism available now over shared, raw pointers, IMHO.

# Conclusion

Though it still feels like it has more syntax than necessary, C++ is not as bad as I remembered it being. I've enjoyed the past 4 months developing in it on a daily basis much more than I have in past jobs. If you check it out again, hopefully it won't be as scary to you either!

Happy coding!