When a type is not a type

Let’s take a look at the following code sample:

Here, p is a std::pair, v is a std::vector, and f is a foo. But there is no such thing as a std::vector type (nor std::pair or foo). std::vector<int> is a type, and std::vector<T> is a type template, but std::vector is just a placeholder that activates a C++17 feature called class template argument deduction (which we will call CTAD for short).

Why CTAD?

Prior to C++17, you had to explicitly specify all the class template arguments, as all of them must be known in order to instantiate the class template. Therefore, the code above would have looked like this:

Since function template argument deduction was available for a long time, the workaround in C++11 was to provide a make_xxx() function that creates instance of the class template, and use auto as placeholder for the type.

Of course, not all the standard types have such a helper function, so it was often the case that users wrote their own make functions. Here you can see an example of a make_vector() variadic function template that creates a vector:

Here is another example for the user-defined class foo:

How it works

When the compiler encounters a declaration of a variable, or a function-style cast using the name of a class template, it builds a set of deduction guides, which is basically fictional functions template representing constructor signatures of a hypothetical class type. These implicit deduction guides created by the compiler can be complemented with user-defined deduction guides. They are then used for performing template argument deduction and overload resolution for initializing objects of this hypothetical class.

Here are several examples (not the complete list) for the implicit deduction types that the compiler constructs for the std::pair class template:

The implicit deduction guides are generated from the constructors of the class template (the default constructor, the copy constructor, and all the other constructor with the type arguments copied in their exact order). If the class template does not have any constructor, a deduction guide is created for a hypothetical default constructor. In any case, a deduction guide for a hypothetical copy constructor is created.

User-defined deduction guides are very similar to function signature with trailing return type but without the auto keyword (after all, they represent fictional constructor signatures). They must be defined in the scope of the class template they apply to. So an example for std::pair could be (although this is actually provided implicitly by the compiler):

Consider the following class type bar that has a constructor using iterators:

The idea is to be able to initialize objects of this type template as follows:

However, this does not work. For instance, the VC++ compiler generates the following errors:

These can be fixed using a user-defined deduction guide as shown below:

The deduction guides don’t have to be templates. Considering the class foo from above, we can have the following deduction guide that forces the compiler to always create instances of foo<std::string> when a char const* is used as argument.

This example can be further applied on the std::pair class template, so that std::string is always used instead of char const*:

Gotchas

CTAD does not take place when the template argument list is present. The following two declarations are both legal:

However, none of the following is valid, as CTAD does not take place:

If you have aggregate types that you want to initialize taking advantage of CTAD then you probably need to define your own deduction guides. Let’s consider the following class template foo. Initializing objects without providing the template argument list does not work.

To leverage CTAD you need to define your own deduction guide, which in this case is as follows:

Conclusions

Class template argument deduction is a useful feature in C++17 that helps developers to simplify the code by avoiding writing the template argument list when initializing objects of class templates (or when performing function-style casts). The compiler provides an implicit set of deduction guides, which are fictional function templates for a hypothetical class and uses them to perform template argument deduction and overload resolution. However, you can extend this set of deduction guides with your own, and in some cases, such as for aggregate types, you need to do so.

See also

You can learn more about this feature from the following articles:

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.