Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
C++17 STL Cookbook

You're reading from   C++17 STL Cookbook Discover the latest enhancements to functional programming and lambda expressions

Arrow left icon
Product type Paperback
Published in Jun 2017
Publisher Packt
ISBN-13 9781787120495
Length 532 pages
Edition 1st Edition
Languages
Tools
Arrow right icon
Author (1):
Arrow left icon
Jacek Galowicz Jacek Galowicz
Author Profile Icon Jacek Galowicz
Jacek Galowicz
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. The New C++17 Features FREE CHAPTER 2. STL Containers 3. Iterators 4. Lambda Expressions 5. STL Algorithm Basics 6. Advanced Use of STL Algorithms 7. Strings, Stream Classes, and Regular Expressions 8. Utility Classes 9. Parallelism and Concurrency 10. Filesystem

Limiting variable scopes to if and switch statements

It is good style to limit the scope of variables as much as possible. Sometimes, however, one first needs to obtain some value, and only if it fits a certain condition, it can be processed further.

For this purpose, C++17 comes with if and switch statements with initializers.

How to do it...

In this recipe, we use the initializer syntax in both the supported contexts in order to see how they tidy up our code:

  • The if statements: Imagine we want to find a character in a character map using the find method of std::map:
       if (auto itr (character_map.find(c)); itr != character_map.end()) {
// *itr is valid. Do something with it.
} else {
// itr is the end-iterator. Don't dereference.
}
// itr is not available here at all
  • The switch statements: This is how it would look to get a character from the input and, at the same time, check the value in a switch statement in order to control a computer game:
       switch (char c (getchar()); c) {
case 'a': move_left(); break;
case 's': move_back(); break;
case 'w': move_fwd(); break;
case 'd': move_right(); break;
case 'q': quit_game(); break;

case '0'...'9': select_tool('0' - c); break;

default:
std::cout << "invalid input: " << c << '\n';
}

How it works...

The if and switch statements with initializers are basically just syntax sugar. The following two samples are equivalent:

Before C++17:

{
auto var (init_value);
if (condition) {
// branch A. var is accessible
} else {
// branch B. var is accessible
}
// var is still accessible
}

Since C++17:

if (auto var (init_value); condition) {
// branch A. var is accessible
} else {
// branch B. var is accessible
}
// var is not accessible any longer

The same applies to switch statements:

Before C++17:

{
auto var (init_value);
switch (var) {
case 1: ...
case 2: ...
...
}
// var is still accessible
}

Since C++17:

switch (auto var (init_value); var) {
case 1: ...
case 2: ...
...
}
// var is not accessible any longer

This feature is very useful to keep the scope of a variable as short as necessary. Before C++17, this was only possible using extra braces around the code, as the pre-C++17 examples show. The short lifetimes reduce the number of variables in the scope, which keeps our code tidy and makes it easier to refactor.

There's more...

Another interesting use case is the limited scope of critical sections. Consider the following example:

if (std::lock_guard<std::mutex> lg {my_mutex}; some_condition) {
// Do something
}

At first, an std::lock_guard is created. This is a class that accepts a mutex argument as a constructor argument. It locks the mutex in its constructor, and when it runs out of scope, it unlocks it again in its destructor. This way, it is impossible to forget to unlock the mutex. Before C++17, a pair of extra braces was needed in order to determine the scope where it unlocks again.

Yet another interesting use case is the scope of weak pointers. Consider the following:

if (auto shared_pointer (weak_pointer.lock()); shared_pointer != nullptr) {
// Yes, the shared object does still exist
} else {
// shared_pointer var is accessible, but a null pointer
}
// shared_pointer is not accessible any longer

This is another example where we would have a useless shared_pointer variable leaking into the current scope, although it has a potentially useless state outside the if conditional block or noisy extra brackets!

The if statements with initializers are especially useful when using legacy APIs with output parameters:

if (DWORD exit_code; GetExitCodeProcess(process_handle, &exit_code)) {
std::cout << "Exit code of process was: " << exit_code << '\n';
}
// No useless exit_code variable outside the if-conditional

GetExitCodeProcess is a Windows kernel API function. It returns the exit code for a given process handle but only if that handle is valid. After leaving this conditional block, the variable is useless, so we don't need it in any scope any longer.

Being able to initialize variables within if blocks is obviously very useful in a lot of situations and, especially, when dealing with legacy APIs that use output parameters.

Keep your scopes tight using if and switch statement initializers. This makes your code more compact, easier to read, and in code refactoring sessions, it will be easier to move around.
You have been reading a chapter from
C++17 STL Cookbook
Published in: Jun 2017
Publisher: Packt
ISBN-13: 9781787120495
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image