Auto Keyword
The auto in C++ is a new keyword which helps to define any variable or function. However, this is always used with an initializer value or variable. At first, the compiler takes the datatype of the initializer on RHS and then deduces the same type for the variable on LHS.
The following example uses auto to define variables.
#include <iostream> //main header
using namespace std; //namespace
int main()
{
auto aa = 0; //int
auto ab = aa; //int
const auto ac = aa; //const int
auto* ad = &aa; //Pointer to int
cout << typeid(decltype(aa)).name() << endl;
cout << typeid(decltype(ab)).name() << endl;
cout << typeid(decltype(ac)).name() << endl;
cout << typeid(decltype(ad)).name() << endl;
return 0;
}
The output is:
i
i
i
Pi
Auto in c++ program is similar to template type deduction in most cases
The auto keyword work in the same way as template works. The auto plays the role of template parameter “T” (as shown below).
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(T tx)
{
cout << typeid(decltype(tx)).name() << endl;
}
int main()
{
auto aa = 0;
auto ab = aa;
const auto ac = aa;
auto* ad = &aa;
//No template initialization with explicit types
funda(aa);
funda(ab);
funda(ac);
funda(ad);
return 0;
}
In the the above program, the template parameter T is deduced by parameters of function calls. For instance, the function “funda” is called with three integer values and one pointer-to-int value.
Please note: There is no explicit instantiation of function template “funda”. The type T is deduced by values which are passed.
This will produce same output as we saw above with auto.
i
i
i
Pi
Specifying auto in C++ like a universal reference
When the program uses && with auto as datatype, then variable becomes like a universal reference. We can initialize such universal referencewith either an L-value specifier or an R-value.
int ix = 7;
auto&& aa = 0; //universal reference deduced to R-value type
auto&& ab = ix; //universal reference deduced to L-value type
The following example, verifies that indeed, a is finally an R-value of type int and b is an L-value.
#include <iostream> //main header
using namespace std; //namespace
int main()
{
int ix;
auto&& aa = 0;
auto&& ab = ix;
cout << typeid(decltype(aa)).name() << endl;
cout << typeid(decltype(ab)).name() << endl;
if(std::is_reference<decltype(aa)>() )
if(std::is_lvalue_reference<decltype(aa)>() )
cout << "L-Vaue" << endl;
else
cout << "R-Vaue" << endl;
if(std::is_reference<decltype(ab)>() )
if(std::is_lvalue_reference<decltype(ab)>() )
cout << "L-Vaue" << endl;
else
cout << "R-Vaue" << endl;
return 0;
}
The output is:
i
i
R-Vaue
L-Vaue
Auto&& is similar to universal reference
The following example rewrites the same logic with universal references. As per the design, the function “funda” is a template function and its parameter T is a universal reference. Nevertheless, the program do not explicitly initialize the function template “funda”. Instead, the compiler uses type of parameter value to deduce the type of template parameter. Finally, the parameter T becomes either an L-value or an R-value reference.
This example is exactly same as the previous one.
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(T&& tx) //tx is universal ref
{
cout << typeid(decltype(tx)).name() << endl;
if(std::is_reference<decltype(tx)>() )
if(std::is_lvalue_reference<decltype(tx)>() )
cout << "L-Value" << endl;
else
cout << "R-Value" << endl;
}
int main()
{
int ix;
funda(0);
funda(ix);
return 0;
}
The output is similar as it was in case of auto&&.
i R-Value i L-Value
Specifier is of initializer list type
When the specifier contains curly braces and comma-separated values, then auto type deduces to become initializer_list type. This happens due to special type-deduction rule for auto. We will see in next section that same is not true for Template type deduction.
#include <iostream> //main header
using namespace std; //namespace
int main()
{
auto aa = {3,4,5};
cout << typeid(decltype(aa)).name() << endl;
return 0;
}
Output:
St16initializer_listIiE
However, the comma-separated values must be of the same type. Otherwise, the compiler shall generate an error.
auto aa = {3, 8.0}; //First is int, second is float
The error is:
initializer_list passed to template function
Although auto keyword deduces to type intializer_list as shown in above example. However, same is not true for template parameters. Actually, there are two kinds of type deductions happening here. First is what kind of list is passed to deduce T1 and second is type of the elements in this list (T2).
Example:
T1 = initializer_list<T2> //Both T1 & T2 to be deduced
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(T tx)
{
}
int main()
{
funda({3, 4});
return 0;
}
The compiler shall generate following error:
For solving this problem, the template parameter should be written as “std::initializer_list<T>”. Now, compiler has to deduce only one type T. The following program now works.
#include <iostream> //main header
using namespace std; //namespace
template<typename T>
void funda(std::initializer_list<T> x)
{
}
int main()
{
funda({3, 4});
return 0;
}
Limitation of auto: Function returning an initializer_list
When the function return type is “auto” and it returns an initializer_list, then auto keyword again cannot deduce types. This is an exception case, here, the type deduction rule for auto works just like templates. Therefore, it throws error just like templates.
#include <iostream> //main header
using namespace std; //namespace
auto funda()
{
return {1, 2, 3};
}
int main()
{
return 0;
}
The error is: