How it works...

In C++11, the standards committee added the ability to automatically deduce a template function's type information based on the arguments that were passed to the function.

Check out this example:

template<typename T>
void foo(T t)
{
show_type(t);
}

The preceding function creates a standard template function that executes a function called show_type() designed to output the type information that it is provided with.

Before C++11, we would use this function as follows:

int main(void)
{
int i = 42;

foo<int>(i);
foo<int>(42);
}

The compiler already knows that the template should define the T type as an integer as that is what the function was provided for. C++11 removes this redundancy, allowing the following:

int main(void)
{
int i = 42;

foo(i);
foo(42);
}

This results in the following output when executed:

Like auto, however, this type deduction gets interesting when r-value references are used, as follows:

template<typename T>
void foo(T &&t)
{
show_type(t);
}

The preceding example defines t as a forwarding reference (also known as a universal reference). The universal reference takes on whatever reference type it is passed. For example, we call this function as follows:

int main(void)
{
int i = 42;
foo(i);
}

We get the following output:

The preceding output shows that the template function was given an l-value reference to an integer. This is because i, in our main function, is an l-value, even though the function appears to be requesting an r-value reference. To get an r-value reference, we must provide an r-value, as follows:

int main(void)
{
int i = 42;
foo(std::move(i));
}

This results in the following output when executed:

As shown in the preceding screenshot, now that we have given the universal reference an r-value, we get an r-value. It should be noted that a universal reference only has the following signature:

template<typename T>
void foo(T &&t)

For example, the following is not a universal reference:

template<typename T>
void foo(const T &&t)

Neither is the following a universal reference:

void foo(int &&t)

Both of the preceding examples are r-value references, and hence require that an r-value be provided (in other words, both of these functions define move operations). A universal reference will accept both an l-value and an r-value reference. Although this seems like an advantage, it has the downside that it is sometimes difficult to know whether your template function has received an l-value or an r-value. Currently, the best way to ensure your template function acts like an r-value reference and not a universal reference is to use SFINAE:

std::is_rvalue_reference_v<decltype(t)>

Finally, it is also possible to perform type deduction on less common types such as C-style arrays, as in this example:

template<typename T, size_t N>
void foo(T (&&t)[N])
{
show_type(t);
}

The preceding function states that we wish to have a C-style array of type T and size N passed to the function and then outputs its type when executed. We can use this function as follows:

int main(void)
{
foo({4, 8, 15, 16, 23, 42});
}

This automatically deduces to an r-value reference of a C-style array of type int and size 6. As shown in this recipe, C++ provides several mechanisms for allowing the compiler to determine what types are leveraged in template functions.

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

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