What's new in Modern C++ (C++17) ?

Photo by Arnold Francisca on Unsplash
Photo by Arnold Francisca on Unsplash

"C++ is such a complicated language, I feel like those *, &, [], () are impossible to read and comprehend." said by one of my ex-coworker. While, C++ is probably the most hatred language according to the internet. But for me personally, I think C++ lands a special place in my heart ❀️.

To be fair to them, I do think that it is somewhat legitimate especially regarding to old C++98. And they all are right to feel so about the old C++. However, C++, just like any other languages, has been changed a lot over time. And this time for C++17 it is no different, it has been changed a lot. If you are not aware of those changes, this article should give you a quick run through.

Let's dig dive in to see what C++17 has to offer.

Type deduction

It has been quite a while (since C++11) that we don't need to declare type if type can be deduced by the right hand-side

Consider this old C++ code;

int i = 0;
double b = 0.5;
float f = 1.0f;

string findMax(int i, int j) {
 if (i > j) {
   return "i";
 } else if (j > i) {
   return "j";
 } else {
   return "equal";
 }
}

In C++11, we can use magical auto

auto i = 0; //deduce to int
auto b = 0.5; //deduce to double
auto f = 1.0f; //deduce to float

auto findMax(int i, int j) { 
  //this check the return type and figure out the type automatically for us 
  //(in this case, it is string) 
 
}

For C++17, it takes the deduction to the whole new level, it also does argument deduction for type with template class. (This is not possible in previous C++).

pair p(2, 4.5);     //deduces to std::pair<int, double> p(2, 4.5);
tuple t(4, 3, 2.5); //same as auto t = std::make_tuple(4, 3, 2.5);
less l;             //same as std::less<void> l;

It also works with collection like vector, set and unordered_map and etc.

vector v{1,2,3}; //vector<int> v{1,2,3}
set s{1,2,3,4};  //set<int>
unordered_map m = {std::pair{"foo", 2}, {"bar", 3}}; //unordered_map<string, int>

As a result, code becomes much more cleaner.

Structure Binding

I think this one is huge and a game changer. Let's look at the old C++ way.

auto pair = make_pair("foo", 1);
tie(s, i) = pair; //s = "foo", and i = 1

Structure binding make use of [ ] to help us destructure the pair, tuple or whatever that implements get<>() template function. (More on this in the later posts 😏).

New and shiny C++ ✨

tuple t{1, "two", false};
auto [one, two, three] = t; //one is 1, two is "two", and three is false

What does this mean? It means that you can work with pair more efficiently.

set s{1};
auto [it, inserted] = s.insert(1);

if (inserted) {
  cout << "value " << *it << "has been inserted" << "\n";
} else {
  cout << "oh no, value" << *it << "is not inserted" << "\n";
}

Working with map is even simpler; This is probably my favorite. πŸ’–

map small_world = {pair{"Japan", "Tokyo"}, {"Thailand", "Bangkok"}, {"South Korea", "Seoul"}};

for (const auto [country, capital] : small_world) {
  cout << country << " : " << capital << "\n";
}

It also works with an array, or struct that contains only non-static public members as well.

Init statement for if/switch

Previously, we have to write something like;

auto v = get_value();
if (condition(v)) {
 //do something on success
} else {
 //do something on failure
}

//we can access v here, even though we don't need it.

Problem? v seems to live longer that it is actually neccessary.

Things are a bit ugly if you want to reuse the naming (as it is a one-time use variable). The prime example for this is to work with stl container.

set s{1,2,3};

auto it = s.find(value);
if (it != s.end()) {
}

auto it1 = s.find(another_value); //πŸ˜“πŸ˜…, name duplicated, let's use 1
if (it1 != s.end()) {
}

auto it2 = s.find(yet_another_value); //πŸ’©πŸ’©πŸ’©, alrighty 2
if (it2 != s.end()) {
}

With this new feature on C++17, we can create variable that use within the if scope and use it right away. How? You can just do this in if.

if (auto it = s.find(value); it != s.end()) {
 //find value in the set s, if it is found, do something here.
} else {
 //it also lives here, value is found!!
}//it is destroyed from this scope.

No more appending your variable name with 1,2,3 .... πŸ’©

Plus, you can use it with structured bindings (following Herb Sutter code):

if (auto [iter, succeeded] = mymap.insert(value); succeeded) {
  use(iter);  //ok
} else {
 //you can also use "succeeded" in else
}

As you can see, I personally think modern C++ is very concise and clean. I hope this article changes more or less your perception about C++. I don't think it is that bad as people has complained anymore.

C++17 is a huge upgrade over the C++ previous versions. C++11 was a huge one but C++17 is a game changer. There are couple more things that I would love to talk about but I think this post is too long already, so I will keep that for my next post then.

Stay tune! πŸ“»