What's new in Modern C++ (C++17) ?
"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! 📻
kittinunf 😼
Clap to support the author, help others find it, and make your opinion count.