How it works...

Class template type deduction is a new feature added in C++17 that provides the ability to deduce the type of a template class from its constructor. Suppose we have the following class template:

template<typename T>
class the_answer
{

public:
the_answer(T t)
{
show_type(t);
}
};

As shown in the preceding code snippet, we have a simple class template that takes a type T during construction and uses a show_type() function to output whatever type it is given. Before C++17, this class would have been instantiated using the following:

int main(void)
{
the_answer<int> is(42);
}

With C++17, we can now instantiate this class as follows:

int main(void)
{
the_answer is(42);
}

The reason this works is that the constructor of the class takes a type T as an argument. Since we provided a numeric integer as the parameter, type T of the class is deduced as an integer. This type deduction includes support for references as well. Check out this example:

template<typename T>
class the_answer
{

public:
the_answer(T &t)
{
show_type(t);
}
};

In the preceding example, our class takes T& as a parameter in the constructor of the class, which allows us to instantiate the class as follows:

int main(void)
{
int i = 42;
the_answer is(i);
}

This results in the following when executed:

As shown in the preceding example, type T of the class was deduced as an l-value reference to an integer. Most of the type deduction rules that apply to function templates also apply to class templates, but there are some exceptions. For example, class template constructors do not support forwarding references (universal references). Consider the following code:

template<typename T>
class the_answer
{

public:
the_answer(T &&t)
{
show_type(t);
}
};

The preceding constructor is not a universal reference; it is an r-value reference, meaning we cannot do the following:

the_answer is(i);

This is not possible as it would be attempting to bind an l-value to an r-value, which is not allowed. Instead, like any other r-value reference, we must instantiate the class using the following:

the_answer is(std::move(i));

Or we can bind it with the following:

the_answer is(42);

The reason universal references are not supported for class template type deduction is the fact that class template type deduction uses the constructor to deduce the type and then fills in the type for the rest of the class based on whatever type was deduced, meaning by the time the constructor is compiled, it looks like this:

class the_answer
{

public:
the_answer(int &&t)
{
show_type(t);
}
};

This defines an r-value reference.

To get a universal reference in the constructor, or any other function, you must use a member function template, which itself can still support type deduction but is not used to deduce any of the class's types. Check out this example:

template<typename T>
class the_answer
{

public:

template<typename U>
the_answer(T &&t, U &&u)
{
show_type(t);
show_type(u);
}
};

In the preceding example, we create a class template with type T, and we define the constructor as a member function template. The constructor itself takes T &&t and U &&u. In this case, however, t is an r-value reference and u is a universal reference, even though they look identical. Both can be deduced by the compiler with C++17 as follows:

int main(void)
{
int i = 42;
the_answer is(std::move(i), i);
}

It should also be noted that the constructor does not have to have any of the types in any specific order for deduction to work. The only requirement is that all of the types are present in the constructor's arguments. For example, consider the following code:

template<typename T>
class the_answer
{

public:
the_answer(size_t size, T &&t)
{
show_type(t);
}
};

The preceding example can be instantiated as follows:

int main(void)
{
the_answer is_2(42, 42);
}

Finally, type deduction also supports more than one template type, as in this example:

template<typename T, typename U>
class the_answer
{

public:
the_answer(const T &t, U &&u)
{
show_type(t);
show_type(u);
}
};

The preceding example creates a class template with two generic types. The constructor for this class creates a const l-value reference to a type T, while also taking an r-value reference to a type U. This class can be instantiated as follows:

int main(void)
{
the_answer is("The answer is: ", 42);
}

This results in the following output:

As shown in the preceding example, both T and U are successfully deduced. 

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset