Tag Archive for C++17

Book “C++ Lambda Story”

A friend of mine published a new book, titled “C++ Lambda Story”. The book explains everything you need to know about lambda expressions in C++.

The book guides you through the evolution of C++ Lambda Expressions so that you can learn it step by step. It starts with C++03 and a motivation to have “ad-hoc” functors, and then moves into the latest C++ standards:

  • C++11 – early days of the feature. You’ll learn about all the essential aspects of lambdas and several tricks you might apply. This is the longest chapter as it needs to cover a lot of topics.
  • C++14 – updates. See how to use generic lambdas and captures with an initializer.
  • C++17 – more improvements, especially by handling ‘this’ pointer and allowing ‘constexpr’. You’ll also learn about the overloaded pattern and how to derive from lambda.
  • C++20 – in this section you’ll see all of the new features adopted for C++20 like template lambdas and how to use them with concepts and constexpr algorithms.

Additionally, throughout the chapters, you’ll learn about the following techniques:

  • Immediately Invoked Functional Expressions (IIFE)
  • How to instrument a default functor to gather extra information
  • Replacing std::bind1st, std::bind2nd and removed functional stuff
  • The Overloaded Pattern and how to inherit from a lambda
  • Passing C++ captureless lambda as a function pointer to C API
  • LIFTING with lambdas
  • Storing lambdas in a container
  • Variadic templates and arguments packs
  • Lambdas and asynchronous execution
  • and many more

The author, Bartłomiej Filipek, is giving a 28% discount to readers of my blog. This offer is valid until 27th of January 2021.
Follow this link to use the coupon if you are interested in this offer.

Share

C++17: std::scoped_lock

C++17 includes an std::scoped_lock (defined in <mutex>) which basically replaces std::lock_guard. Both are RAII classes that automatically unlock a mutex when they are destroyed. The only difference between the two is that the new scoped_lock can handle multiple mutexes automatically, while lock_guard is limited to a single mutex.

Using the scoped_lock RAII class is straightforward. Here is an example:

std::mutex mutex1;
// ...
std::scoped_lock<std::mutex> sl(mutex1);

The scoped_lock class template is a variadic template supporting any number of mutexes:

std::mutex mutex1;
std::recursive_mutex mutex2;
// ...
std::scoped_lock<std::mutex, std::recursive_mutex> sl(mutex1, mutex2);

The declaration of the scoped_lock can be simplified thanks to C++17 template argument deduction for constructors:

std::mutex mutex1;
std::recursive_mutex mutex2;
// ...
std::scoped_lock sl(mutex1, mutex2);


Share

C++17: Template Argument Deduction for Constructors

C++ always had support for template argument deduction when calling function templates. For example, suppose you have the following function template:

template<typename T>
void MyFunction(const T& data) { /* ... */}

It’s a function template with one template parameter. When you call this function, you do not have to specify the type of the template parameter explicitly. C++ automatically deduces the type T based on the arguments passed to the function. For example:

MyFunction(5);
MyFunction("Test");

If you really want to specify the template type explicitly you do this as follows:

MyFunction<int>(5);
MyFunction<std::string>("Test");

Up to C++17, this deduction only worked for function templates. C++17 adds support for template argument deduction for constructors of class templates.

For example, before template argument deduction for constructors, when you instantiate any Standard Library container you have to specify the type of the elements you want to store in the container:

std::vector<int> ints = { 11,22,33 };

With C++17’s template argument deduction for constructors, you can omit the explicit specification of the type. Of course, this only works when you instantiate the container and immediately initialize it with initial elements, otherwise the compiler has no data from which it can deduce any types. So, the above example can be written in C++17 as follows:

std::vector ints = { 11,22,33 };

This seemingly little feature has quite an impact. Because of the lack of this feature in previous versions of C++, the Standard Library included helper function templates. For example, a std::pair<int, std::string> can be constructed in the following two ways (pre-C++17):

std::pair<int, std::string> myPair1{ 11, "Eleven" };
auto myPair2 = std::make_pair(12, "Twelve");

Either you directly use the std::pair class and explicitly specify the template parameters, or, you use the specially-introduced std::make_pair() function template that can do the template argument deduction.

With C++17’s new deduction rules, you can forget about the helper function templates, and simply write the following:

std::pair myPair3{ 13, "Thirteen" };

Similarly for other helper function templates that were introduced earlier like std::make_move_iterator() to make a std::move_iterator(), and so on.

If you want to add support for template argument deduction to your own classes, then you will have to write your own so-called deduction guides, but that’s for a future post.

Share

C++17: Standard Library Algorithms: Changes and Additions

The C++14 Standard Library already contains a wealth of different kinds of algorithms. C++17 adds the following new algorithms and new options to existing algorithms:

  • std::sample()
  • std::for_each_n()
  • Specialized searchers for std::search()
  • Generalized sum algorithms:
    • exclusive_scan()
    • inclusive_scan()
    • transform_exclusive_scan()
    • transform_inclusive_scan()
    • reduce()
    • transform_reduce()
  • Parallel algorithms
  • gcd() / lcm()

I wrote an article for the Visual C++ Team Blog explaining what’s new and what has changed related to algorithms in the C++17 Standard Library in more details.

You can read the article over here.

Share

C++17 in Detail by Bartłomiej Filipek


C++17 provides developers with a nice selection of new features to write better, more expressive code.

Bartłomiej Filipek has released a book titled “C++17 in Detail” that describes all significant changes in the language and the Standard Library. What’s more, it provides a lot of practical examples so you can quickly apply the knowledge to your code. The book brings you exclusive content about C++17. Additionally, the book provides insight into the current implementation status, compiler support, performance issues and other relevant knowledge to boost your current projects.

The book is a work in progress and is currently 90% finished. The final version should be coming in at around 250 pages. Even though it’s not finished yet, if you buy early, you’ll get free updates later.

Here are the features you’ll learn from reading this book:

Part One: C++17 Language features

  • Fixes and deprecation
  • Language clarification
  • General language features
  • Templates
  • Attributes

Part Two: C++17 The Standard Library

  • std::optional
  • std::variant
  • std::any
  • std::string_view
  • String operations
  • Filesystem
  • Parallel STL
  • Other Changes

Part Three: More Examples and Use Cases

  • Refactoring with std::optional
  • Using if constexpr
  • … and more planned

You can find more information on the book’s website.

Share

C++17 Removed and Deprecated Features

My friend Marius Bancila has posted a nice overview about existing C++ features that have been either removed by C++17 (after being deprecated in a previous version) or that have been deprecated in C++17 so that they could be removed sometime in the future. His list is not exhaustive, but the most important of these removed or deprecated features are mentioned. Removed features include:

  • throw(typeid)
  • std::auto_ptr
  • std::random_shuffle
  • std::unary_function / binary_function
  • std::ptr_fun
  • std::bind1st, bind2nd
  • And more

Deprecated features include:

  • std::uncaught_exception
  • throw()
  • std::unary_negate / binary_negate
  • std::not1 / not2
  • The <codecvt> header
  • And more

Read his blog post here.

Share

C++17: Attributes

C++17 introduces three new code attributes:

  • [[fallthrough]]
  • [[maybe_unused]]
  • [[nodiscard]]

The first one was discussed in detail in my C++17: Fallthrough in switch statements blog post. The others are briefly explained below.

[[maybe_unused]]

This attribute can be used to mark variables or functions that might be unused to avoid the compiler from generating a warning for such variables and functions.

For example, suppose you have code as follows:

bool DoSomething()
{
    // ... do something ...
    return true;
}

int main()
{
    bool result = DoSomething();
    assert(result);
}

When you build a release build of your code, the assert() macro becomes a no-operation. As such, in release builds, the result variable is unused, while in debug you need the result variable for the assert(). To avoid the compiler warning, use the [[maybe_unused]] attribute as follows:

int main()
{
    [[maybe_unused]] bool result = DoSomething();
    assert(result);
}

This attribute can also be used on functions. Maybe you have a function that is only executed in debug. In such a case, you can mark the function with the attribute. For example:

static bool OnlyUsedInDebug() { return true; }

int main()
{
    assert(OnlyUsedInDebug());
}

When compiling with gcc, the compiler gives the following warning:

warning: ‘bool OnlyUsedInDebug()’ defined but not used [-Wunused-function]

The avoid this warning, use [[maybe_unused]]:

[[maybe_unused]] static bool OnlyUsedInDebug() { return true; }

[[nodiscard]]

This attribute can be used to make sure the caller of a function does not just discard the return value of that function. For example:

bool DoSomething() { return true; }

int main()
{
    DoSomething();
}

The user is not doing anything with the return value of the DoSomething() function, but the compiler will not give any warning about this. Now, the following code adds the [[nodiscard]] attribute:

[[nodiscard]] bool DoSomething() { return true; }

When you try to call it as in the previous main() function, the compiler will complain with something as follows:

warning C4834: discarding return value of function with ‘nodiscard’ attribute

Share

C++17: std::string_view

If you have to write a function that accepts a string, what type of parameter will you use? You have a couple of choices:

  • const std::string&: downside is that whenever you call the function, an std::string is expected. If you pass a const char*, an std::string instance will be created implicitly for you.
  • const char*: downside is that if your caller has an std::string, they will have to use c_str() on it to pass a const char* to your function.
  • A const char* overload and a const std::string& overload: downside is that you have to implement two functions. This solution quickly becomes unwieldy if you are writing a method that accepts more than one string as parameter. For example, suppose your method requires two strings as parameters. If you want to overload the method to provide all possible combinations of input argument types, then you end up writing four versions: (const char*, const char*), (const char*, const string&), (const string&, const char*), and (const string&, const string&). This only becomes worse with even more string parameters.

C++17 makes it easy by introducing a new type called std::string_view. From now on, if you are writing a function that accepts a string, use an std::string_view as parameter type. No need to use an std::string_view reference. A string_view is very cheap to copy, so it’s perfectly fine to pass by value. Basically, a string_view just contains a pointer to a string, and its length. A string_view parameter accepts any kind of string, such as a C++ std::string, a C-style const char* string, and a string literal, all without any copying involved!

std::string_view is defined in the <string_view> header. It has a similar interface as the well-known std::string class. However, since a string_view is a read-only view of a string, only the const operations of std::string are supported on a string_view. Additionally, a big advantage of a string_view is that it will never copy a string.

Here is a very brief example of its usage:

#include <iostream>
#include <string_view>

using namespace std;

void ProcessString(string_view myString)
{
    cout << myString; if (myString.size() >= 4)
    {
        cout << "   (Substring: " << myString.substr(2, 2) << ")";
    }
    cout << endl;
}

int main()
{
    string myString1 = "Hello";
    const char* myString2 = "C++";
    ProcessString(myString1);  // C++ string
    ProcessString(myString2);  // C-style string
    ProcessString("World!");   // String literal
}

The output of this piece of code is as follows:

Hello   (Substring: ll)
C++
World!   (Substring: rl)

My book, Professional C++, 4th Edition, explains the std::string_view class in a bit more details. Additionally, it explains all new C++17 features, and much more.

Share

C++17: Fallthrough in switch statements

A C++ switch statement allows execution to fall through from one case to the next case when a break statement is missing. For example:

#include <iostream>

using namespace std;

enum class Mode
{
    Default,
    Custom
};

class Parameters {};

Parameters AskUserForCustomParameters()
{
    cout << "Asking user for custom parameters..." << endl;
    return Parameters();
}

void Process(const Parameters& parameters)
{
    cout << "Processing parameters..." << endl;
}

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
    case Mode::Default:
        Process(parameters);
        break;
    }
}

int main()
{
    DoSomething(Mode::Custom);

    cout << endl;

    DoSomething(Mode::Default);
}

Of course, in this case we could just have used an if statement, but that wouldn’t demonstrate fallthrough in switch statements.

If you look at the code, the case for Mode::Custom does not contain a break statement. Some compilers will issue a warning for this kind of fallthrough because it might be unintended and a source of bugs. C++17 introduced a [[fallthrough]] attribute that you can use to specify in code that a fallthrough is intentional and should not generate a warning. Here is the modified fragment:

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
        [[fallthrough]];
    case Mode::Default:
        Process(parameters);
        break;
    }
}

You don’t need to use the [[fallthrough]] attribute for empty switch cases. No compiler will issue a warning for fallthrough with empty case statements. For example:

enum class Mode
{
    Default,
    Custom,
    Standard
};

void DoSomething(Mode mode)
{
    Parameters parameters;  // Create a default set of parameters
    switch (mode)
    {
    case Mode::Custom:
        AskUserForCustomParameters();
        [[fallthrough]];
    case Mode::Standard:
    case Mode::Default:
        Process(parameters);
        break;
    }
}

My book, Professional C++, 4th Edition, explains all new C++17 features, and much more.

Share

C++17: std::byte

What is it?

C++17 introduced a new type: std::byte.

Previously, when you needed to access raw memory, you would use an unsigned char or a char data type. However, these data types give the impression that you are working with characters or with numeric values. The new std::byte data type does not convey character or arithmetic semantics, it is just a collection of bits. As such, it’s ideal to represent raw memory. An std::byte only supports initialization from an integral type, and can be converted back to an integral type using std::to_integer(). The only other operations supported are bit-wise operations. Both std::byte and std::to_integer() are defined in <cstddef>.

How to use it?

Let’s look at some short examples. First, here is how you can initialize an std::byte:

#include <cstddef>

int main()
{
    std::byte myByte{ 2 };
}

Next, you can use std::to_integer() to convert an std::byte back to an integral type of your choice:

#include <iostream>
#include <cstddef>

int main()
{
    std::byte myByte{ 2 };
    std::cout << std::to_integer<int>(myByte) << std::endl;
}

The following bitwise operations are supported: <<, >>, |, &, ^, ~, and <<=, >>=, |=, &=, and ^=. Here is an example:

#include <iostream>
#include <cstddef>

using namespace std;

void PrintByte(const byte& aByte)
{
    cout << to_integer<int>(aByte) << endl;
}

int main()
{
    byte myByte{ 2 };
    PrintByte(myByte);	// 2

    // A 2-bit left shift
    myByte <<= 2;
    PrintByte(myByte);	// 8

    // Initialize two new bytes using binary literals.
    byte byte1{ 0b0011 };
    byte byte2{ 0b1010 };
    PrintByte(byte1);	// 3
    PrintByte(byte2);	// 10

    // Bit-wise OR and AND operations
    byte byteOr = byte1 | byte2;
    byte byteAnd = byte1 & byte2;
    PrintByte(byteOr);	// 11
    PrintByte(byteAnd);	// 2
}

Why use it?

You might wonder what the difference is with just using the existing uint8_t instead of std::byte. Well, std::byte is really just a bunch of un-interpreted bits. If you use uint8_t, you are actually interpreting the bits as an 8-bit unsigned numerical value, which might convey the wrong semantics. Also, std::byte will not allow accidental arithmetic on it, while uint8_t does.

Raw memory buffers and interoperability with legacy C-style API’s

If you need a buffer of raw memory, then you can use std::vector<std::byte>.

Additionally, if you need to pass such a buffer to a legacy C-style API accepting, for example, an unsigned char*, then you will have to add a cast. Here is a brief example:

void SomeCApi(unsigned char* buffer, unsigned int size)
{
    for (unsigned char index = 0; index < size; ++index) {
        buffer[index] = index;
    }
}

int main()
{
    std::vector<std::byte> buffer{ 100 };
    SomeCApi(reinterpret_cast<unsigned char*>(&buffer[0]), buffer.size());

    for (const auto& element : buffer) { PrintByte(element); }
}

Conclusion

When you are working with raw bits, use an std::byte instead of an unsigned char or char data type.

Share

“Professional C++, 4th Edition” Released

It has been a lot of work, but I’m proud to announce my fourth edition of “Professional C++” 🙂
It is published by Wiley/Wrox.
And already available at Amazon.

Official Description

Get up to date quickly on the new changes coming with C++17

Professional C++ is the advanced manual for C++ programming. Designed to help experienced developers get more out of the latest release, this book skims over the basics and dives right in to exploiting the full capabilities of C++17. Each feature is explained by example, each including actual code snippets that you can plug into your own applications. Case studies include extensive, working code that has been tested on Windows and Linux, and the author’s expert tips, tricks, and workarounds can dramatically enhance your workflow. Even many experienced developers have never fully explored the boundaries of the language’s capabilities; this book reveals the advanced features you never knew about, and drills down to show you how to turn these features into real-world solutions.

The C++17 release includes changes that impact the way you work with C++; this new fourth edition covers them all, including nested namespaces, structured bindings, string_view, template argument deduction for constructors, parallel algorithms, generalized sum algorithms, Boyer-Moore string searching, string conversion primitives, a filesystem API, clamping values, optional values, the variant type, the any type, and more. Clear explanations and professional-level depth make this book an invaluable resource for any professional needing to get up to date quickly.

  • Maximize C++ capabilities with effective design solutions
  • Master little-known elements and learn what to avoid
  • Adopt new workarounds and testing/debugging best practices
  • Utilize real-world program segments in your own applications

C++ is notoriously complex, and whether you use it for gaming or business, maximizing its functionality means keeping up to date with the latest changes. Whether these changes enhance your work or make it harder depends on how well-versed you are in the newest C++ features. Professional C++ gets you up to date quickly, and provides the answers you need for everyday solutions.

Share

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