C++ Template Specialization / Partial and Full - c++

I was trying to understand Template Partial and Full Specialization and have this below code:
#include <iostream>
using namespace std;
//Template in C++is a feature. We write code once and use it for any data type including user defined data types.
//What if we want a different code for a particular data type - Template Specialization
template <class T, class Y>
void fun(T a, Y b)
{
cout << "The main template fun(): " << a << " " << b << endl;
}
//If a specialized version is present, compiler first checks with the specialized version and then the main template.
//Compiler first checks with the most specialized version by matching the passed parameter with the data type(s) specified in a specialized version.
template<>
void fun(const char* a, const char* b)
{
cout << "Specialized Template for int type: " << a << " " << b << endl;
}
//Partial specialization allows to specialize only some arguments of a class template, as opposed to explicit full
template<class T>
void fun(T a, int b)
{
cout << "Partial Template specialization for int type: " << a << " " << b << endl;
}
int main()
{
fun<int, int>('a', 10);
fun< const char*, const char*>("Hello", "Morning");
fun<float, float>(10.14, 19.89);
}
Note that in main I am specifying the data types and below is the output:
The main template fun(): 97 10
Specialized Template for int type: Hello Morning
The main template fun(): 10.14 19.89
But when I execute the main code below way:
int main()
{
fun('a', 10);
fun("Hello", "Morning");
fun(10.14, 19.89);
}
This is the output I get:
Partial Template specialization for int type: a 10
Specialized Template for int type: Hello Morning
The main template fun(): 10.14 19.89
So what does the actual C++ Template Partial / Full specialization states - do we need to specify the data types in template argument to invoke - also in many websites I have seen following signature for Partial specialization:
template<class Y, const char*>
void fun(Y a, const char* b)
Rather than
template<class Y>
void fun(Y a, const char* b)
Similarly for full specialization - what is the exact way to write and call Partial / Full template specialized function / class?

The comments cover the general principles, but there are specific questions here worth answering. First, a lemma: the template parameters of a function template need not be related to its parameter types:
template<class T>
T make_1() {return T(1);}
auto v=make_1<std::vector<std::string>>(); // {""}
template<class T,class U>
void f(std::conditional_t<sizeof(T)<sizeof(U),int,long> i);
This is why your “partial specialization” is not a candidate for fun<int, int>(…). The template arguments aren’t just instructions for how to build the parameter list one by one, but rather must match the actual template parameters for the function template in question. There’s only one of those, so they don’t match. True partial specializations still use the primary template’s template parameter list; they just match against certain patterns in the list of arguments for it.
Meanwhile,
template<class Y, const char*>
void fun(Y a, const char* b)
is valid but doesn’t mean what you think for the same reason: const char* in the template parameter list introduces an unnamed (non-type) template parameter of type const char*, so that you’d have to use it like
char global;
void g() {fun<char,&global>('a',"foo");}
(with no deduction for Y!), only to have that &global ignored entirely,

Related

Constant template arguments for functions

I have the defined the following types:
template <typename TType>
struct symbol{};
template <typename TAtom, unsigned IDim>
struct tensor_type {};
template <unsigned IDim>
using real = tensor_type<double, IDim>;
template <unsigned IDim>
using index = tensor_type<int, IDim>;
I now want to call functions with instances of the type symbol like this:
template <template<typename TType, unsigned IDim> typename TTemplate, typename TType, unsigned IDim>
void f(symbol<TTemplate<TType, IDim>>* sym) {
std::cout << "higher dimension" << std::endl;
}
int main() {
symbol<real<0>>* p1 = new symbol<real<0>>();
symbol<index<0>>* p2 = new symbol<index<0>>();
symbol<real<1>>* p3 = new symbol<real<1>>();
symbol<index<5>>* p4 = new symbol<index<5>>();
f(p1); //dimension 0
f(p2); //dimension 0
f(p3); //higher dimension
f(p4); //higher dimension
}
This works fine and well. But now I want to differ between symbols of the dimension 0 and all the other dimensions, but I do not know how to properly declare an overloaded function properly with the templates to achive this.
I have tried the following:
template <typename TType>
void f(symbol<TType<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: ‘TType’ is not a template during compilation and
template <template<typename TType> typename TTemplate, typename TType>
void f(symbol<TTemplate<TType>>* sym) {
std::cout << "dimension 0" << std::endl;
}
with error: wrong number of template arguments (2, should be 1).
I cannot include the code for both dimensions in one function because in the actual implementation types with dimension 0 have different attributes than the one with higher dimensions, so even if I use a case distinction in the function the compiler complains that some passed arguments do not have the needed attribute.
One possiblity is to declare a function for every possible type for dimension 0 like this
void f(symbol<real<0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
but since I have multiple types in the actual implementation and all the functions would look exactly the same (besides the signature) I wanted to avoid redundant code.
Is there some way to achive this more elegant?
In your shown example, there is only one relevant class template: tensor_type
The others declared with using are just aliases for this one. They do not define new class templates or types.
So with that it is enough to simply specify that template in the overload:
template <typename T>
void f(parameter_symbol<tensor_type<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
If you have multiple templates, some of which you haven't shown, then
template <template<typename, unsigned> typename TTemplate, typename T>
void f(parameter_symbol<TTemplate<T, 0>>* sym) {
std::cout << "dimension 0" << std::endl;
}
will work. Note that the kinds of template parameter in the template template parameter template<typename, unsigned> typename TTemplate must match the class template that you are going to use as argument. (As one exception, you can also replace unsigned with auto.)
If you have multiple templates with different kinds of template parameters, then you need define such overloads for all cases.
If the dimension is not always in the same position in the parameter list, then this approach doesn't work.
It would be simpler to have the relevant classes provide a constexpr static member variable idim which indicates the dimension, or a constexpr static function returning it. Or alternatively a specialized free variable template or function template.
Then you can have a function overload taking any type and constrain it through SFINAE or if you can use C++20, a requires class:
template <typename T>
requires(T::idim == 0)
void f(parameter_symbol<T>* sym) {
std::cout << "dimension 0" << std::endl;
}

non-type template parameter vs function parameter

I wrote a trivial example, which is somehow compiles.
#include <iostream>
using namespace std;
template <int A>
void func()
{
cout << 1 + A << endl;
return;
}
int main()
{
// I can not even use this strange func()
int a = 1; func(a); // this does not compile
func(1); // this does not compile as well
return 0;
}
This example frustrates me a lot:
First, I gave non-type template parameter to template, but did not provide any parameters (in parentheses) to function itself. Looks like template parameter becomes the function parameter, but why?
Second, even though it compiles, I can not find a way to use this template, see my comments in main.
And third, what is the reason for existence of template functions with non-type template parameter of integral type? How is it any different from having a regular function?
int A is not a function parameter, it's a template parameter. func takes no arguments and you instantiate/call it like this:
func<1>(); // compile-time constant needed
Please review C++ function templates. You can't use template parameters in the way you want.
On the other hand, having a type template parameter and one function parameter:
template <typename A>
void func(A a)
{
cout << 1 + a << endl;
}
will make your program valid. Maybe that's what you wanted.
Edit:
To your request, here's a usage of such non-type function template parameter:
template <size_t S>
void func(const int (&array)[S])
{
cout << "size of the array is: " << S << endl;
}
or std::array version:
template <size_t S>
void func(std::array<int, S> const& array)
{
cout << "size of the array is: " << S << endl;
}
S here is deduced to the size of the passed array.

Function template specialisation of overloaded function template

#include <iostream>
using namespace std;
template <typename A, typename B> void test(A a, B b) { cout << "first" << endl;}
template <typename A> void test(A a, int b) { cout << "second" << endl;}
template <> void test(int a, int b) { cout << "special" << endl;} //this is a specific instantiation, of which template?
int main(){
test(2,3); //got: "special"
test<int>(2,3); //got: "special"
test<int, int>(2,3); //got: "first". So specialisation is linked to second template, why?
}
What is the rule for selecting which template the explicit specialization is an instantiation of? It seems like it could be ambiguous. From the output of the last function call it is indicated to me that the specialisation is for the second function template definition (the explicit template arguments select the first template, but the specialisation is not used - so it must be a specialisation of the second template), why exactly? It seems like any relevant info should be in [14.7.3] of N3337 but to the best of my ability I could not find what I was looking for. Might the program even be in error by doing this?
Granted it might be rare that someone would have to worry about this but I'm still curious as to why it happens.

template function overloading with template type-parameter-list

Please help to understand below 3 different syntax's
#include <iostream>
using namespace std;
template <typename T>
class Demo {
public:
void print_type(){
if(is_same<int,T>())
cout << "Int type" << endl;
if(is_same<float,T>())
cout << "float type" << endl;
}
};
//1. fun
template<typename T>
void fun(Demo<T> a){
cout << "In <T>fun(Demo<T>)" <<endl;
a.print_type();
}
//2. fun
template<typename T=int,class D=Demo<T> >
void fun(D a){
cout << "In <T,class>fun(Demo)" <<endl;
a.print_type();
}
// 3. fun
template<typename T=int,template <typename T> class D=Demo >
void fun(D<T> a){
cout << "In <T,template>fun(Demo<T>)" <<endl;
}
int main()
{
fun(Demo<int>());
fun(Demo<float>());
return 0;
}
the function calls in main are calling the first version of fun
if I comment out any two definitions of fun, the third one is called
What is the difference between the three fun definitions? How can I call all 3 without commenting out any of them?
Let's take a look at our three functions:
template <typename T> void fun(Demo<T> ); // (1)
template <typename T, typename D> void fun(D ); // (2)
template <typename T, template <typename> class D> void fun(D<T> ); // (3)
Let's consider the second. D will be deduced from the first argument, but T does not appear in any of the function parameters - it is a non-deduced context. As such, it must be specified explicitly by the caller in order to be considered. You do not specify it, so it's a deduction failure and is thrown out of the overload set.
Now (1) and (3) are both viable candidates for arguments of type Demo<int> and Demo<float>. In both cases, the two functions take identical arguments that are exact matches for the caller and are both function templates - so the last tiebreaker is to consider which function template is more specialized (this is also called function template partial ordering). (1) can only accept types that look like Demo<T>. (3) can accept Demo<T> but also OtherTemplate<T> or YetAnotherClass<T>. It's more generic - so we prefer to call (1). The rules for this are pretty complicated, but you can do more searching if you're interested. A simpler example might be:
template <typename T> void foo(T ); // (4)
template <typename T> void foo(T* ); // (5)
foo(new int(42));
Both candidates are viable, (4) matches with T = int* and (5) matches with T = int. But (4) matches anything whereas (5) only matches pointers, which makes it more specialized and thus the best viable candidate.
If you comment out (2), nothing changes - it wasn't a viable candidate anyway. But if you comment out (1), then we call (3) because (3) becomes the only viable candidate (and thus, trivially, the best viable candidate).

C++ - rules around function template instantiation

I am learning function templates just now and I wanted to research a bit rules of function templates instantiation. So I've written the following code:
#include <iostream>
template <typename>
int check(int x) {
return x * 2;
}
int main() {
std::cout << check<double>(10) << std::endl; // #1
std::cout << check<>(10) << std::endl; // #2
std::cout << check(10) << std::endl; // #3
return 0;
}
The lines #1, #2 and #3 are not compiled all together, each try I leave only one of them and comment the rest.
So, when #1 is enabled, I have no compilation errors and the correct answer "20" is printed. As I understand, "check<"double">" invokes template instantiation mechanism so "int check(int)" function is really created (the type of template parameter doesn't have any influence).
When #3 is enabled, I have a compilation error "error: no matching function for call to 'check(int)'", which is reasonable, since I am trying to call to "check(int)" function which doesn't exist. My question is regarding #2 case: in this case I get the same "error: no matching function for call to 'check(int)'". Shouldn't call "check<>(10)" trigger template instantiation mechanism as well?
You don't put any template parameter in the <>. How can compiler know which template function to instantiate? Please notice that your template function is:
template <typename>
int check(int x)
If you change it to this:
template <typename T>
int check(T x)
then check<>(10) should be fine because the complier can know the type from the parameter.
I have to say that usually template doesn't go that way:(
Try this one:
template<typename T>
T check(T x){
return x*2;
}
The only template instantiation you'll get is with
check<double>(10)
The others don't instantiate the template. Also for the full power of function templates include the template argument as
template<typename T>
T check(T x) {
return x*2;
}
Then using the power of template argument deduction you can call
check(10.0); // instantiates check<dobule>
check(3); // instantiates check<int>
or
a = MyObject();
check(a); // instantiates check<MyObject>
#2 would be valid if the template argument could be deduced.
#include <iostream>
template <class T>
void foo(T t)
{
std::cout << "Template " << t << '\n';
}
void foo(int n)
{
std::cout << "Not template " << n << '\n';
}
int main()
{
foo(10); //calls non-template function
//because matching non-template preferred over function templates
foo<>(10); //calls the template function, empty brackets indicate you want
//the template overload, type is deduced from passed argument.
}
Another possibility, if the function template had default arguments (only legal in C++11).
template <class T = void>
int foo()
{
return 10;
}
int main()
{
return foo<>(); //same as foo<void>();
}
If you don't pass a template argument, it is equivalent to looking for the function without a template argument (i.e. it doesn't instantiate the template). The added <> means nothing in this case because there are is no overloading.
Since the function you have doesn't actually use the template for anything and the compiler thus can't infer the type T, but if you write it like this:
template <typename T>
T check(T x) {
return x * 2;
}
Then all 3 cases will work, since it infers the type T. See https://stackoverflow.com/a/797632/888641 for more explanation.
Note: You can use typename or class, they do the exact same thing here.