Explicit template specialization issue - c++

template <class T>
class Test
{
public:
template<class U> void f(); //generic function
template<> void f<char>(); //Specialization for char.
};
template <class T>
template<class U>
void Test<T>::f() //Definition of generic function
{
}
template<>
template<> void Test<char>::f<char>(){} //Definition of specialization.
int main()
{
Test<char> ob1;
ob1.f<char>(); //Works fine.
Test<int> ob2;
ob2.f<char>(); //Produces linker error.
}
Linker error is
error LNK2019: unresolved external symbol "public: void __thiscall
Test<int>::f<char>(void)"
My requirement is: I should be able to pass any type to Test class and any type to function f(). I should be able to use all combinations of types like below.
Test f()
--------------
int char
char int
int int
I can solve the error by defining another function like below.
template<>
template<> void Test<int>::f<char>(){}
But then what is the use of making Test class as Template ? How to make it work for all combinations ?

C++03, §14.7.3/2:
An explicit specialization shall be declared in the namespace of which the
template is a member, or, for member templates, in the namespace of which
the enclosing class or enclosing class template is a member.
An explicit specialization of a member function, member class or static data
member of a class template shall be declared in the namespace of which the
class template is a member.
Therefore you should declare your specialization outside of a class, for example:
template <class T>
class Test
{
public:
template<class U> void f(); //generic function
};
template <class T>
template <class U>
void Test<T>::f() {} //Definition of generic function
template<>
template<>
void Test<char>::f<char>(){} //Specialization.
int main()
{
Test<char> ob1;
ob1.f<char>();
Test<int> ob2;
ob2.f<char>();
}

The problem that you are facing is that you have declared the specialization of f for char in the Test template, and that is incorrect. The compiler is not detecting the error, but it is getting confused and interpreting that you want to provide the specialization of f for char in all template instantiations:
template <typename T>
struct Test {
template <typename U> void f();
template <> void f<char>(); // <- Incorrect
};
When you write Test<int> the compiler instantiates the template and is (mistakenly) accepting it and interepreting that there is an specialization of f for char in Test<int>.
Just remove the line, and you will get the code to compile. It will use the specialization only for Test<char>::f<char>(), and I am not sure whether that is what you want.
If your intention is specializing f for char with all instantiating types, that is not allowed. When you define a template specialization, all enclosing templates be specialized. A common work around is not providing an specialization but a different overload of the member function:
template <typename T>
struct Test {
template <typename U> void f( U );
void f( char );
};
But that won't help you much there, as you cannot provide different overloads for the same arguments (in your case no arguments). Also, in your case you must explicitly call the template to differentiate, and code that explicitly requests the template would not pick up the overload:
int main() {
Test<int> t;
t.f<char>(); // will call template, not "void f(char)"!!
}
Without more details on what you really want to achieve I cannot think on other potential solution to the issue.

My requirement is: I should be able to pass any type to Test class and any type to function f(). I should be able to use all combinations of types like below.
Why do you need an explicit specialization? Why do you unnecessarily want to make your code complex?
The following works for all combinations that you have listed.
template <class T>
class Test
{
public:
template<class U> void f();
};
template <class T>
template<class U>
void Test<T>::f(){}
int main()
{
Test<char> ob1;
ob1.f<char>(); //Works fine. T = char, U = char
Test<int> ob2;
ob2.f<char>(); //Works fine T = int, U = char
}

Related

template class method specialization using concepts

There is a template class A with template parameter T. I want this class to have a method f if T is of integral types. The class A also has a lot of other methods, so I don't want to have specialization of overall A.
I understand that this problem can be solved using inheritance, but my question is about concepts and requirements.
This code
template <typename T>
struct A {
void f();
};
template <>
void A<int>::f() {}
works as I expect. It makes implementation of f for the int type only. If I try to call A<std::string>{}.f(); it generates a linker error as expected.
But if I write
template <typename T>
struct A {
void f();
};
template <std::integral T>
void A<T>::f() {}
either
template <typename T> requires std::is_integral_v<T>
void A<T>::f() {}
the method f is generated for all types, so calling A<std::string>{}.f(); does not give any error.
Also this works
template <typename T>
struct A {
void f() {}
};
template <>
void A<std::string>::f() = delete;
but this
template <typename T>
struct A {
void f() {}
};
template <std::integral T>
struct A<T>::f() = delete;
gives compilation error, namely redefinition of f.
P.S. It seems such constructions are not allowed at all, but g++ just ignores concepts in definition of method f.
There are four syntactical methods of applying constraints to a function.
Type constraint in a template parameter list; template< Concept TypeID >.
Requires clause after a template parameter list; template< class TypeID > requires constexpr-andor-requires-expression.
Constraint on auto in an abbriviated function template; void f(Concept auto id);.
Requires clause after a function declaration; template< class TypeID > void f() requires constexpr-andor-requires-expression.
The function you want to constrain doesn't have a template parameter list so you can't use methods 1 and 2. Method 3 essentially generates template parameters. So that leaves method 4.
#include <concepts>
template< class T >
struct A {
void f() { /* do something */ }
void g() requires std::integral<T> { /* do something */ }
void h() requires std::integral<T>;
template< std::integral U = T >
void i() { /* do something */ }
};
template< class T >
void A<T>::h() requires std::integral<T> { /* do something */ }
int main() {
A<double> dblA;
dblA.f();
// dblA.g(); // A<double>::g() is not declared or defined
// dblA.h(); // A<double>::h() is not declared or defined
// dblA.i(); // A<double>::h<double>() is not declared or defined
dblA.i<int>(); // A<double>::h<int>() is declared and defined
A<int> intA;
intA.f();
intA.g(); // A<int>::g() is declared and defined
intA.h(); // A<int>::h() is declared and defined
intA.i(); // A<int>::h<int>() is declared and defined
//intA.i<double>(); // A<int>::h<double>() is not declared or defined
return 0;
}
You have to add a new template parameter, which will default to T and to which you can add your constraint of std::integral<>. This compiled with gcc10.3.0 and clang12.0.0, other versions you will have to test yourself.
The code:
#include <concepts>
#include <string>
template <typename T>
struct A
{
template <typename U = T>
requires std::integral<U>
void f();
};
template <typename T>
template <typename U>
requires std::integral<U>
void A<T>::f()
{
}
int main()
{
A<int> a;
a.f();
A<std::string> s; // This works
// s.f(); // Compilation error: constraint not satisfied
return 0;
}

Template Specialization with Template for Static Function

I have a template that inherits from another template, with itself as the second template's template parameter. The inherited template defines a static function:
template<class T> class A
{
public:
static void foo();
};
template<class T> class B : public A<B>
{
};
Now I want to implement the static function for the class A specialized with B, but with B not specialized. But I can't figure out how to declare the template. I'm not even sure if this is possible. My first try was:
template<class T> void A<B<T>>::foo()
{
}
But this gives the error:
"Nested name specifier 'A<B<T>>::" for declaration does not refer into a class, class template or class template partial specialization"
I've tried different things like adding "template<>" in front but none of those worked. I am able to compile this:
template<> void A<B<int>>::foo()
{
}
As well as this:
template<class T> void A<T>::foo()
{
}
Is this an attempt at partial specialization? My first impression is no (there are no templates with multiple parameters where I want to specialize one of them). Rather, I want to specialize a template with another template that is not specialized. Is this possible, and if so what is the proper syntax?
This is indeed partial specialization. You cannot partially specialize just a method, you must partially specialize the whole class. See this answer. You might try implementing foo in a separate helper struct and partially specializing that struct instead.
Here is an example using a helper struct.
#include <iostream>
template<class T> struct t_helper
{
static void foo()
{
std::cout << "Not B<T>\n";
}
};
template<class T> class A
{
public:
static void foo() {
t_helper<T>::foo();
}
};
template<class T> class B {};
// Specialize the behavior of A<T>::foo() for all B types
template<class T>
struct t_helper<B<T>>
{
static void foo()
{
std::cout << "Is B<T>\n";
}
};
int main()
{
A<int>::foo(); // Prints "Not B<T>\n"
A<B<int>>::foo(); // Prints "Is B<T>\n"
return 0;
}

partial template member specialization

Given the following definitions:
template <typename T>
class A {
public:
void f();
};
template <typename T>
void
A<T>::f()
{}
template <typename T>
class B {};
How would I partially specialize A<B<T>>::f, i.e. f for some B<T>? I'm basically looking for the right magic to substitute the ??? below
template <???>
void
A<B<T>>::f()
{}
You can have an explicit specialization, from [temp.expl.spec]:
An explicit specialization of any of the following:
— ...
— member function of a class template
— ...
can be declared by a declaration introduced by template<>
That is:
template <>
void A<B<int>>::f() {
std::cout << "B\n";
}
But you cannot have a partial specialization of a member function of a class template. You would have to partially specialize the entire class:
template <typename T>
class A<B<T>> {
public:
void f() {
std::cout << "B\n";
}
// ... all other members you want in A<B<T>> ...
};
You cannot partially specialize a member function (nor in fact any function). You need to partially specialize the whole class:
template<typename T>
class A<B<T>>
{
// implement member functions for this specialization here
};
If you must have:
template <typename T>
void A<B<typename T>>::f() {}
then your only choice is to partially specialize A.
template <typename T> class A<B<T>>
{
public:
void f();
};
C++11 has Alias Templates, allowing you do do something like:
template<T>
using AB = A<B<T>>;
Then you can refer to AB<T> instead of A<B<T>>.
Unfortunately, you can't use that for specialization..
So seems to me the answer to your question is: You can't do that, but it's a shame.

Why the difference in syntax between explicit and partial specialization?

Example:
template <typename T, typename U>
struct A {
void Print() {}
};
template <>
void A<int, float>::Print() {} // Okay
template <typename T>
void A<T, char>::Print() {} // Will produce error
Question:
I know that you have to define the class template partial specialization in the above code for it to work and I also know from the standard that The members of the class template partial specialization are unrelated to the members of the primary template (§ 14.5.5.3). However, why the difference in syntax between a explication specialization and a partial specialization?
You cannot specialize function templates partially, only fully.
The first instance makes use of the fact that member functions of class templates are themselves function templates, and thus the restriction applies to them.
When you partially-specialize the class template, you have an entirely new class template, which you have to define anew.
template <typename T>
void A<T, char>::Print() {} // Will produce error
You are:
re-defining a function (it has already been defined when declared void Print() {}, you see there are {}.
with a template argument list that doesn't match the declaration: template <typename T, typename U> void Print()
In fact, even if you haven't defined the function when declared it, you will still have an error since your declaration and definition do not match, the compiler will not be able to find a definition for the original template, or a declaration for the specialized template.
A specialized template function for a function that is related to a struct must have a specialized struct as well, This code works:
template <typename T, typename U>
struct A {
void Print() {}
};
template <>
void A<int, float>::Print() {} // Okay
template <typename T>
struct A<T,char>
{
void Print();
};
template <typename T>
void A<T,char>::Print() {}
Because template function has been declared in it's template struct.

Is it possible to overload a template class?

I found that template method could be overloaded, can I do the same on template classes? If 2 template classes match a template class instantiation, we can use the parameter type in the constructor to deduce which one to use.
template <typename T>
class A{
A(T){}
};
template <typename T>
class A{
A(T*){}
};
int main(){
A<int*> a((int*)0);
A<int> a((int*)0);
return 0;
}
No. This is not allowed. Instead class template can be specialized (including partial specialization). This pretty much achieves the effect of overloading (which is only for functions)
Note that template parameters can not be deduced from constructor arguments.
template<class T> struct X{
void f(){}
};
template<class T> struct X<T*>{
void f(){}
};
int main(){
X<int> x;
x.f(); // calls X<T>::f
X<int *> xs;
xs.f(); // calls X<T*>::f
}