How it works...

Neither auto nor typename in C++ provides the ability to get a variable's type and create new types using that information. To better explain why you might want to do this, let's look at the following example:

template<typename FUNC>
auto question(FUNC &&func)
{
auto x = func() + 10;
return x;
}

We start our example with a function that takes any function as an input and returns the result of this function plus 10. We can then execute this function as follows:

short the_answer()
{
return 32;
}

int main(void)
{
auto i = question(the_answer);
show_type(i);
}

As shown in the preceding example, we pass the question() function a pointer to another function that returns short. On executing this function, we store the results and then we use a function called show_type(), which is designed to output what type the provided type is. This results in the following:

The problem with this example is the fact that the type that is returned is not the same type that we were given. C++ is allowed to increase the size of any variable as needed, and often does with shorts, especially when you attempt to perform arithmetic on a short with numeric values as numeric values are represented as integers.

Since we do not know what the return type of the provided function will be in the question() function, there is no way to fix this issue. Enter decltype(). To explain, let's update our example to address the preceding problem:

template<typename FUNC>
auto question(FUNC &&func)
{
decltype(func()) x = func() + 10;
return x;
}

As shown in the preceding example, we replaced auto with decltype(func()). This tells the compiler to get the return type of func() and use that type to define x. As a result, the compiler converts this template into the following function:

short question(short(*func)())
{
short x = func() + 10;
return x;
}

This happens instead of the following, which was expected initially:

int question(short(*func)())
{
int x = func() + 10;
return x;
}

This then results in the following output when executed:

As shown in the preceding screenshot, we are now getting the proper type returned from our question() function. With C++14, we can take this example a bit further and write it this way:

template<typename FUNC>
constexpr auto question(FUNC &&func) -> decltype(func())
{
return func() + 10;
}

In the example in the preceding code snippet, we converted the question() function into constexpr, which allows the compiler to optimize out the function call, replacing a call to question() with the func() + 10 statement. We also remove the need for a stack-based variable by explicitly telling the compiler what type we wish the function to return using the -> decltype() function return syntax. It should be noted that this syntax is needed as the following would not compile:

template<typename FUNC>
constexpr decltype(func()) question(FUNC &&func)
{
return func() + 10;
}

The preceding code will not compile because the compiler does not have the definition of func() yet, and hence it doesn't know what its type is. The -> syntax addresses this by placing the return type at the end of the function definition instead of at the front.

The decltype() specifier can also be used in place of auto as follows:

int main(void)
{
decltype(auto) i1 = 42;
decltype(auto) i2{42};

show_type(i1);
show_type(i2);
}

This results in the following output:

In this example, we create two integers using decltype(auto) and initialize them to 42. In this specific case, decltype(auto) and auto operate exactly the same. Both define the placeholder type as an integer as both are initialized using a numeric value, which, by default, is int.

Like auto, you can decorate decltype(auto) with CV qualifiers (that is, const/volatile) as follows:

int main(void)
{
decltype(auto) i1 = 42;
const decltype(auto) i2 = 42;
volatile decltype(auto) i3 = 42;
const volatile decltype(auto) i4 = 42;

show_type(i1);
show_type(i2);
show_type(i3);
show_type(i4);
}

This results in the following output:

The real magic of decltype(auto) is how it handles references. To demonstrate this, let's start with the following example:

int main(void)
{
int i = 42;

int i1 = i;
int &i2 = i;
int &&i3 = std::move(i);

show_type(i1);
show_type(i2);
show_type(i3);
}

When executed, we see the following output:

i1 = int
i2 = int&
i3 = int&&

As shown in the preceding example, we have created an integer, an l-value reference to an integer, and an r-value reference to an integer. Let's see what happens if we attempt to use auto instead of int as follows:

auto a1 = i1;
auto a2 = i2;
auto a3 = std::move(i3);

show_type(a1);
show_type(a2);
show_type(a3);

We then see the following output:

a1 = int
a2 = int
a3 = int

As shown in the preceding example, we are only given integers. All of the references were removed. The only way to get references with auto is if we explicitly define them as follows:

auto a4 = i1;
auto &a5 = i2;
auto &&a6 = std::move(i3);

show_type(a4);
show_type(a5);
show_type(a6);

This results in the following, expected, output:

a4 = int
a5 = int&
a6 = int&&

The problem with having to add the extra & operators to explicitly define the reference type is that this assumes that, in our template code, we actually know what the references should be. If this information is not available, we would have no way of writing a template function and know whether we could create an l-value or r-value reference, likely resulting in a copy.

To overcome this, decltype(auto) not only inherits the type and CV qualifiers during initialization, it also inherits the references as follows:

decltype(auto) d1 = i1;
decltype(auto) d2 = i2;
decltype(auto) d3 = std::move(i3);

show_type(d1);
show_type(d2);
show_type(d3);

The preceding code, when executed, results in the following:

d1 = int
d2 = int&
d3 = int&&

As shown in the preceding example, decltype(auto) can be used to inherit all of the type information of the value it is being initialized to, including referenceness.

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

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