Tag Archive for C++17

C++17: Initializers for if & switch statements

Two small, but very useful C++17 features are initializers for if and switch statements. These can be used to prevent polluting the enclosing scope with variables that should only be scoped to the if and switch statement. The for statement already supports such initializers since the beginning.
For example, suppose you are using a map to store the number of times a certain name occurs:

#include <cstddef>
#include <map>
#include <string>
#include <iostream>

int main()
{
    std::map<std::string, size_t> nameCounters{
        {"Marc", 5},
        {"Bob", 12},
        {"John", 3}
    };

    auto result = nameCounters.find("Bob");
    if (result != cend(nameCounters))
        std::cout << "Count: " << result->second << std::endl;
}

In this example I’m searching for a key in the map, and if the key is found, I output the associated value to the console. The result of calling find() is stored in result, but this variable is only used inside the if statement.
With C++17, you can initialize the result variable with an if statement initializer so that the result variable is only known inside the if statement and does not pollute the enclosing scope with unnecessary variables:

#include <cstddef>
#include <map>
#include <string>
#include <iostream>

int main()
{
    // ... Initialize map as before ...

    if (auto result = nameCounters.find("Bob"); result != cend(nameCounters))
        std::cout << "Count: " << result->second << std::endl;
}

Similar kind of initializers are supported for switch statements:

switch (<initializer>; <expression>) { /* ... */ }
Share

C++17: Inline Variables

Before C++17, if your class had any non-const static data members, you had to allocate memory for them. For example, suppose you have the following class definition:

class MyClass
{
private:
    static int s_anInt;
    static std::string s_aString;
};

Then your source file should contain the following:

int MyClass::s_anInt = 42;
std::string MyClass::s_aString = "Hello World!";

This is annoying.
C++17 now supports inline variables which allow you to write the MyClass definition as follows:

class MyClass
{
private:
    static inline int s_anInt = 42;
    static inline std::string s_aString = "Hello World!";
};

This feature makes it easier to write header only classes that contain non-const static data members.

At the time of this writing, Microsoft Visual C++ 2017 does not yet support inline variables.

Share

C++17: Direct vs Copy List Initialization

C++11 introduced the uniform initialization syntax using braced initializer lists. You have direct list initialization which does not include an equal sign, for example:

int myInt{42};

and you have copy list initialization which uses the equal sign, for example:

int myInt = {42};

The auto type deduction rules have changes in C++17. With C++17, if you use copy list initialization, then an initializer_list<> is deduced, for example:

auto initList1 = {42};
auto initList2 = {42, 84, 126};

All values in the braced initializer list must be of the same type. The following does not compile:

auto initList2 = {42, 84.42};

When you use direct list initialization, then a value is deduced. For example, the following deduces an int:

auto anInt{42};

This also means that the following causes a compilation error because more than 1 initialization value is given:

auto ints{42, 84, 126};

This is the new C++17 behavior. With C++11/C++14, all of the above would deduce to an initializer_list<int>.

This changed behavior is included since Microsoft Visual C++ 2015.

Share

C++17: Nested Namespaces

The second post in this series of C++17 features highlights a tiny but very useful new feature called nested namespaces.

Using multi-level namespaces in C++ has always been a pain. Suppose you are writing a game and you have a namespace called Core::Graphics::Rendering. Before C++17 you had to resort to something as follows

namespace Core {
    namespace Graphics {
        namespace Rendering {

            class PostProcessor
            {
            };

        }
    }
}

This leaves you with a very big indentation scheme. You could try to make it look a bit better as follows:

namespace Core { namespace Graphics { namespace Rendering {

    class PostProcessor
    {
    };

}}}

But this does not always plays nice with the auto formatting functionality of your IDE.

Say hello to C++17 nested namespaces. Now you can simply write the following:

namespace Core::Graphics::Rendering {

    class PostProcessor
    {
    };

}

Nested namespaces are supported in Microsoft Visual C++ 2015 since Update 3.

Share

C++17: Structured Bindings

This is a first post in a series of short articles on new C++17 features. These articles will not contain all little details of the new features being presented, but they give you an idea about what new functionality has been added to C++17.

This first article discusses structured bindings. They allow you to declare multiple variables with a single statement that are initialized with values from a pair, tuple, array, or even a struct.

Let’s look at an example using a tuple. Suppose you have the following tuple consisting of a double, string, and integer (note the use of the user-defined std::string literal s):

auto myTuple = std::make_tuple(1.1, "Hello"s, 42);

Before C++17, if you wanted to decompose this tuple into three separate variables, you could use tie() as follows:

double theDouble;
std::string theString;
int theInt;
std::tie(theDouble, theString, theInt) = myTuple;

That’s 4 lines of code just to decompose the tuple.

C++17 structured bindings allow you to do this with a single line:

auto[theDouble, theString, theInt] = myTuple;

It also works for pairs, and this comes in handy for example when using insert() to insert values into a map. insert() returns a pair with as first element an iterator to the inserted element, and as second element a Boolean that is true if the element was inserted successfully. You can decompose that pair as follows:

std::map<int, int> myMap{ {1, 11}, { 2,22 }, { 3,33 }};
auto[it, success] = myMap.insert({ 4,44 });

If you want to use a range-based for loop to iterate over the elements, you could do it as follows:

for (const auto& element : myMap) {
    std::cout << element.first << ": " << element.second << std::endl;
}

The type of the element variable is a pair containing the key and the value of the elements in the map.

Again, you can use structured bindings to make this shorter and easier to understand:

for (const auto&[key, value] : myMap) {
    std::cout << key << ": " << value << std::endl;
}

Structured bindings are available in Microsoft Visual C++ 2017 since Update 3.

Share

Milestone: VS 2015 Update 2’s Standard Library is C++11, C++14, and C++17-so-far Feature Complete

The C++ Standard Library in the Visual Studio 2015 Update 2 release is C++11, C++14, and C++17-so-far feature complete. This includes all features from C++17 that are currently in that standard proposal: SFINAE-friendly result_of, improvements to pair and tuple, shared_mutex, removal of deprecated iostreams aliases, variable templates for type traits (is_same_v, …), as_const(), logical operator type traits (conjunction, …), owner_less<>, variadic lock_guard, and additions to <chrono>: floor(), ceil(), round(), and abs().
More information, including a detailed list can be found here.

Share