The C++17 allows using auto as template parameter. The parameter shall become a non-type in this case. This means, the template parameter shall hold the actual value of specified template argument. Therefore, we cannot use such template parameter as datatype inside the body. The auto parameter is valid for both function as well as class templates. There are certain restriction on this feature, which we will discuss subsequently.
//N is a type
template<typename N>
class MainFunda { }
//N has a value
template<auto N>
class MainFunda { }
Basic example with function template
Following simple example shows how to use auto template type with a function template. In each call, the N takes the value of different kind of data types at compile-time. Accordingly, the compiler instantiates the template with different types of values of N.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
void funda()
{
cout << N << endl;
}
int main()
{
funda<2>(); //N is int
funda<'x'>(); //N is char
funda<0xb>(); //N is hex int
funda<true>(); //N is bool 1
funda<false>();//N is bool 0
funda<3l>(); //N is long int
funda<6u>(); //N is unsigned int
funda<-2>(); //N is signed int with -ve val
return 0;
}
Output
Constant L-values as arguments
Even the L-values which are constant at compile time are valid. Therefore, the code like, the following shall also work. However, if the “x” is not constant then compiler shall generate an error during compilation.
const int x = 5;
funda<x>();
Invalid cases to instantiate a template using auto parameter
Certain data types do not qualify for a non-type template parameter. For instance, the auto parameter shall not accept a float or a double value. These are generally invalid values to become a template type. Probably, the additional complexity involved regarding how a machine may store the significant digits may cause such types to become unsuitable. Therefore, the following code shall generate an error.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
void funda()
{
cout << N << endl;
}
int main()
{
funda<5.2>(); //error
return 0;
}
Output
Another invalid case is of providing run-time addresses to instantiate such templates. A pointer having an address cannot instantiate the template with non-type parameter. This is because, the compiler cannot know the address of a variable before the program starts executing. Therefore, such address cannot be used in angular brackets.
The following code shall cause error.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
class MainFunda
{
public:
MainFunda()
{
cout << N << endl;
}
};
int main()
{
int p;
MainFunda<&p> m1;
return 0;
}
Output
Using Non-type as template parameter for class
The similar concept works with class template. Following example demonstrates this.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
class MainFunda
{
public:
MainFunda()
{
cout << N << endl;
}
};
int main()
{
MainFunda<2> m1;
MainFunda<'x'> m2;
MainFunda<0xb> m3;
MainFunda<true> m4;
MainFunda<false>m5;
MainFunda<3l> m6;
MainFunda<6u> m7;
MainFunda<-2> m8;
return 0;
}
Output
Partial template specialization of template with non-type parameter
Just like the case of partial specialization with template types, similarly, there is concept with non-types. Basically, auto is a generic placeholder for any datatype. Therefore, in a partial specialization, specifying a particular type generates partially specialized templates. For Example, any particular type, like, a bool or an int, etc. for the same parameter name shall create partial specialization. Therefore, in such cases, the compiler instantiates partial version with data argument of this specific data-type.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
class MainFunda
{
public:
MainFunda()
{
cout << N << endl;
}
};
template<bool N> class MainFunda<N>
{
public:
MainFunda()
{
cout << "Boolean : " << N << endl;
}
};
int main()
{
MainFunda<2> m1;
MainFunda<'x'> m2;
MainFunda<0xb> m3;
MainFunda<true> m4; //calls partial version
MainFunda<false>m5; //calls partial version
MainFunda<3l> m6;
MainFunda<6u> m7;
MainFunda<-2> m8;
return 0;
}
Output
Full Template Specialization
To specialize the template class, we may specify exact value in place of template parameter. This is exactly similar as in case of template with typename paramtype. The following code, specializes same template on 2 integer values and one boolean value.
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
class MainFunda
{
public:
MainFunda()
{
cout << N << endl;
}
};
template<> class MainFunda<2>
{
public:
MainFunda()
{
cout << "Number Two " << endl;
}
};
template<> class MainFunda<-2>
{
public:
MainFunda()
{
cout << "Number Minus Two " << endl;
}
};
template<> class MainFunda<false>
{
public:
MainFunda()
{
cout << "Boolean False " << endl;
}
};
int main()
{
MainFunda<2> m1; //calls specialization
MainFunda<'x'> m2;
MainFunda<0xb> m3;
MainFunda<true> m4;
MainFunda<false>m5; //calls specialization
MainFunda<3l> m6;
MainFunda<6u> m7;
MainFunda<-2> m8; //calls specialization
return 0;
}
Output
Class Template Argument Deduction
Argument deduction is a new feature in C++17. In this feature, the compiler can automatically deduce parameter type from argument list during instantiation. Please read the page for full understanding. There is no need to explicitly specify the type in angular brackets. However, in case of non-type parameter, things work differently. The non-type parameter deduction happens in relation to a type and not independently.
The following example shows that the class has one typename and one non-type (auto) parameters. Once, the compiler deduces typename T using argument, the non-type also gets the value.
#include <iostream> //main header
using namespace std;//for namespace
template<typename T, auto N>
class MainFunda
{
public:
MainFunda(T (&x)[N])
{
cout << x << ", size=" << N << endl;
}
};
int main()
{
MainFunda m1{"abc"};
MainFunda m2{"abcdef"};
MainFunda m3{"abcdef aldjfalj"};
return 0;
}
Output
In conclusion, the deduction shall not work if there is only non-type (auto) parameter. In the following example, the compiler cannot automatically, deduce the data-type with given argument (integer 2)
#include <iostream> //main header
using namespace std;//for namespace
template<auto N>
class MainFunda
{
public:
MainFunda(int x)
{
cout << "MainFunda(int)" << endl;
}
};
int main()
{
MainFunda m1{2};
return 0;
}