These articles are written by Codalogic empowerees as a way of sharing knowledge with the programming community. They do not necessarily reflect the opinions of Codalogic.

The Rise of 'auto'

By: Pete, June 2021

Originally auto was a largely superfluous keyword that you could forget about without any great loss. In fact in C++11 its original meaning was deprecated. But as each new C++ standard comes out auto plays a bigger and bigger role.

Below is a program that shows most of its modern uses (see https://godbolt.org/z/n3Tfnz54s). I'm hoping the comments provide sufficient explanation.

#include <iostream>
#include <string>
#include <tuple>
#include <concepts>

// C++ 11 - placeholder type specifier - the variable type is deduced from the initializer
auto i = 10;
auto f = 2.2;

// C++11 - trailing return type
auto f1() -> int
{
    return 0;
}

// C++14 - The return type is deduced from the operand of its first return statement
auto f2()
{
    return 0;
}

// C++17 - in a structured binding declaration
/*Given a function*/ auto x1() { return std::tuple{ 1, 2.2 }; }
void f3()
{
    auto [a, b] = x1();
    std::cout << __FUNCTION__ << ": a = " << a << ", b = " << b << "\n";
}
// Called with: f3(); - Output is: f3: a = 1, b = 2.2

// C++17 - in the parameter declaration of a non-type template parameter
// where the template argument is a value not a type
template<auto V>
void f4()
{
    std::cout << __FUNCTION__ << ": " << V << "\n";
}
// Called with: f4<12>(); - Output is: f4: 12
// Called with: f4<2.5>(); - Output is: f4: 2.5

// C++20 - Abbreviated function template - An alternative template notation
// Equivalent to: template< typename T > void f5( T t )
void f5( auto t )
{
    std::cout << __FUNCTION__ << ": " << t << "\n";
}
// Called with: f5( 12 ); - Output is: f5: 12
// Called with: f5( 2.5 ); - Output is: f5: 2.5

// C++20 - In concepts - The concept is specified before 'auto'
void f6( std::integral auto i )
{
    std::cout << __FUNCTION__ << ": " << i << "\n";
}
// Called with: f6( 10'200'300'400ll ); - Output is: f6: 10200300400
// Can't acll with f6( "test" ); // std::integral concept prevents this

// In C++23 - To create a new temporary variable
/*Given a function*/ void x2( int & a ) { ++a; }
/*Given a function*/ void x3( int && a ) { ++a; }
void f7()
{
    int a = 1;
    x2( a );    // a is incremented
    std::cout << __FUNCTION__ << ": " << a << " ";
    x3( auto{a} );  // A temporary is passed to x3() so a is not incremented
    std::cout << a << "\n";
}
// Called with: f7(); - Output is: f7: 2 2

// In C++23 - To derive a type based on an expression
/* Not implemented yet
void f8( auto a, auto b )
{
    std::cout << auto{a + b}( 7.5 );
}
*/

int main()
{
    f1();
    f2();
    f3();
    f4<12>();
    f4<2.5>();
    f5( 12 );
    f5( 2.5 );
    f6( 10'200'300'400ll ); // Using C++14 digit separator
    //f6( "test" ); // std::integral concept prevents this
    f7();
}

The output is:

f3: a = 1, b = 2.2
f4: 12
f4: 2.5
f5: 12
f5: 2.5
f6: 10200300400
f7: 2 2