
C++ Auto & Decltype
A great introduction to C++ 11 can be found within C++ Primer (5th Edition) . Within the book the author shares two new keywords to c++ auto and decltype. The compiler uses these keywords and the rest of the line to declare a variable that follows the keyword; for example auto x = 0.
The keyword auto declares the variable only by type. The keyword will ignore a reference and strip anyconst and volatile qualifiers; for example:
[code language=”cpp”]
// Declare another integer
int i = 0;// Set the reference to the integer
int &ri = i;// Declare another integer and set it to the reference value
auto j = ri;// Declare another reference and set it to the reference ri
auto &rk = ri;
// Increment the integer
i++;
// At this point j is an integer that is equal
// to zero and ri, rk and i is equal to 1
[/code]
In the example I used integers but one can use double, float, class and struct objects, etc.
The keyword decltype goes a bit further than auto and declares a variable’s type by what is in its parameter;decltype(x * i). All operations within the decltype’s parameter does NOT get performed, but is only used by the compiler to determine the type of the variable.
[code language=”cpp”]
// Declare an integer and initialize it
int i = 3;// Declare a double and initialize it
double d = 2.0;// Declare another double e and initialize it to 2.0
decltype(i * d) e = d;// At this point i is 3 and d and e is 2.0.
// The operation (i * d) was never performed
[/code]
Another feature of decltype is where one can use function calls to determine a variable type; for exampledecltype(foo()) x. Again, the operations within the decltype’s parameter does NOT get performed, so foo() is never called within the example. However, the function call’s return type is used to declare the variable x.
You can also use a feature of C++ 11 that takes advantage the combination of auto and decltype in a method called trailing types.
[code language=”cpp”]
////////////////////////////////////////////////////////////////////////////////
// The class outlines a useless object that manages a list of floats
class CMyFloatList
{
// Declare a list that will manage a list of floats
std::list<float> _fList;
////////////////////////////////////////////////////////////////////////////
// The function will return to the caller the first element within the list.
auto begin() const -> decltype(_fList.begin())
{
// Return to the caller the first element within the list
return(_fList.begin());
} // End of auto begin() const -> decltype(_fList.begin())
}; // End of class MyFloatList
[/code]
The real power of trailing types and the keywords of auto and decltype comes into play with templates.
[code language=”cpp”]
////////////////////////////////////////////////////////////////////////////////
// The function returns to the caller the indexed element within the array.
template <typename T>
auto GetElement(T &tArray, unsigned int uiIdx) -> decltype(tArray[uiIdx])
{
// Return to the caller the indexed element
return(tArray[uiIdx]);
} // End of auto GetElement(…) -> decltype(tArray[uiIdx])
[/code]
The following is the final example of decltype and auto in action:
[code language=”cpp”]
// Example.cpp
// Include the standard input/output library
#include <stdio.h>
// Declare and open the program
int main()
{
// Declare another integer
int i = 0;
// Set the reference to the integer
int &ri = i;
// Increment the integer
i++;
// Print to the user the reference integer (should be 1)
printf("The value of \’ri\’ is %d \n\n", ri);
// Declare another integer and set it to the reference value
auto j = ri; // auto only uses the type of the value – it will drop const and
// volatile qualifiers and ignore the the reference type
// Declare another reference integer and set to a refrence of i
decltype(ri) k = ri;
// Declare another reference integer and set to a refrence of i
auto &l = ri;
// Increment the integer
i++;
// Print to the user the reference integer (should be 2)
printf("The value of \’ri\’ is %d \n", ri);
// Print to the user the reference integer (should be 1)
printf("The value of \’j\’ is %d \n", j);
// Print to the user the reference integer (should be 2)
printf("The value of \’k\’ is %d \n", k);
// Print to the user the reference integer (should be 2)
printf("The value of \’l\’ is %d \n\n", l);
// Increment the integer
i++;
// Print to the user the reference integer (should be 3)
printf("The value of \’ri\’ is %d \n", ri);
// Print to the user the reference integer (should be 1)
printf("The value of \’j\’ is %d \n", j);
// Print to the user the reference integer (should be 3)
printf("The value of \’k\’ is %d \n", k);
// Print to the user the reference integer (should be 3)
printf("The value of \’l\’ is %d \n\n", l);
// Return to the 0.S. a good exit
return(0);
} // End of int main()
[/code]
One final note, with experience working with these keywords I have found that it is bad practice to use them arbitrarily. I have found them very useful with writing templates. I have also found them useful using them sparingly within small functions with variable types of long names. For good readable code I only recommend using these keywords in these instances.
Please, keep your comments family friendly and respectful of each other and the author.