Book Image

C++17 STL Cookbook

By : Jacek Galowicz
Book Image

C++17 STL Cookbook

By: Jacek Galowicz

Overview of this book

C++ has come a long way and is in use in every area of the industry. Fast, efficient, and flexible, it is used to solve many problems. The upcoming version of C++ will see programmers change the way they code. If you want to grasp the practical usefulness of the C++17 STL in order to write smarter, fully portable code, then this book is for you. Beginning with new language features, this book will help you understand the language’s mechanics and library features, and offers insight into how they work. Unlike other books, ours takes an implementation-specific, problem-solution approach that will help you quickly overcome hurdles. You will learn the core STL concepts, such as containers, algorithms, utility classes, lambda expressions, iterators, and more, while working on practical real-world recipes. These recipes will help you get the most from the STL and show you how to program in a better way. By the end of the book, you will be up to date with the latest C++17 features and save time and effort while solving tasks elegantly using the STL.
Table of Contents (18 chapters)
Title Page
Credits
About the Author
About the Reviewer
www.PacktPub.com
Customer Feedback
Preface
Index

Letting the constructor automatically deduce the resulting template class type


A lot of classes in C++ are usually specialized on types, which could be easily deduced from the variable types the user puts in their constructor calls. Nevertheless, before C++17, this was not a standardized feature. C++17 lets the compiler automatically deduce template types from constructor calls.

How to do it...

A very handy use case for this is constructing std::pair and std::tuple instances. These can be specialized and instantiated and specialized in one step:

std::pair  my_pair  (123, "abc");       // std::pair<int, const char*>
std::tuple my_tuple (123, 12.3, "abc"); // std::tuple<int, double,
                                        //            const char*>

 

How it works...

Let’s define an example class where automatic template type deduction would be of value:

template <typename T1, typename T2, typename T3>
class my_wrapper {
    T1 t1;
    T2 t2;
    T3 t3;

public:
    explicit my_wrapper(T1 t1_, T2 t2_, T3 t3_) 
        : t1{t1_}, t2{t2_}, t3{t3_}
    {}

    /* … */
};

Okay, this is just another template class. We previously had to write the following in order to instantiate it:

my_wrapper<int, double, const char *> wrapper {123, 1.23, "abc"};

We can now just omit the template specialization part:

my_wrapper wrapper {123, 1.23, "abc"};

Before C++17, this was only possible by implementing a make function helper:

my_wrapper<T1, T2, T3> make_wrapper(T1 t1, T2 t2, T3 t3)
{
    return {t1, t2, t3};
}

Using such helpers, it was possible to have a similar effect:

auto wrapper (make_wrapper(123, 1.23, "abc"));

Note

The STL already comes with a lot of helper functions such as that one: std::make_shared, std::make_unique, std::make_tuple, and so on. In C++17, these can now mostly be regarded as obsolete. Of course, they will be provided further for compatibility reasons.

 

There's more...

What we just learned about wasimplicit template type deduction. In some cases, we cannot rely on implicit type deduction. Consider the following example class:

template <typename T>
struct sum {
    T value;

    template <typename ... Ts>
    sum(Ts&& ... values) : value{(values + ...)} {}
};

This struct, sum, accepts an arbitrary number of parameters and adds them together using a fold expression (have a look at the fold expression recipe a little later in this chapter to get more details on fold expressions). The resulting sum is saved in the member variable value. Now the question is, what type is T? If we don't want to specify it explicitly, it surely needs to depend on the types of the values provided in the constructor. If we provide string instances, it needs to be std::string. If we provide integers, it needs to be int. If we provide integers, floats, and doubles, the compiler needs to figure out which type fits all the values without information loss. In order to achieve that, we provide an explicit deduction guide:

template <typename ... Ts>
sum(Ts&& ... ts) -> sum<std::common_type_t<Ts...>>;

This deduction guide tells the compiler to use the std::common_type_t trait, which is able to find out which common type fits all the values. Let's see how to use it:

sum s          {1u, 2.0, 3, 4.0f};
sum string_sum {std::string{"abc"}, "def"};

std::cout << s.value          << '\n'
          << string_sum.value << '\n';

In the first line we instantiate a sum object with constructor arguments of type unsigned, double, int, and float. The std::common_type_t returns double as the common type, so we get a sum<double> instance. In the second line, we provide an std::string instance and a C-style string. Following our deduction guide, the compiler constructs an instance of type sum<std::string>.

When running this code, it will print 10 as the numeric sum and abcdef as the string sum.