Is following design possible?:
template <typename T>
class Test{
public:
template <typename Z>
void doSomething();
//rest of things
private:
T obj;
//some things
};
Now if it was possible I'd do some explicit specializations for doSomething so that at the end I'd have some versions like below:
void doSomething<int>(){
//do something
}
void doSomething<double>(){
//do something
}
...etc
which seems impossible I can't find any syntax to do the job then I thought maybe the design should be as it follows so that all template arguments should be passed to template class itself:
template <typename T,typename Z>
class Test{
public:
void doSomething();
//rest of things
private:
T obj;
//some things
};
Then I tried partial specialization which didn't even compile:
template <typename T>
void Test<T,int>::doSomething(){
//do something
}
template <typename T>
void Test<T,double>::doSomething(){
//do something
}
...etc
I got the following errors for explicit specialization:
error#1:template argument list following class template name must list parameters in the order used in template parameter list.
error#2:'Container1' :too few template arguments.
In order to explicitly specialize doSomething you have to also explicitly specialize Test.
From 14.7.3/18 :
In an explicit specialization
declaration for a member of a class
template or a member template that
appears in namespace scope, the member
template and some of its enclosing
class templates may remain
unspecialized, except that the
declaration shall not explicitly
specialize a class member template if
its enclosing class templates are not
explicitly specialized as well.
You cannot explicitly specialize a member template unless its enclosing class templates are also explicitly specialized.
So only something like this will work:
template<> template<>
void Test<int>::doSomething<int>()
{
}
you can always make the function inline
template <class T>
class Test
{
public:
template <class Z>
void doSomething() { cout << "default" << endl; }
template<>
void doSomething<int>() { cout << "int" << endl;}
template<>
void doSomething<double>() { cout << "double" << endl; }
private:
T obj;
};
I think this one is picky. I suppose you can't do it, read this.
Not sure if this is a bug in g++ but this compiles and produce what I expect.
#include<typeinfo>
#include<iostream>
template<typename T>
class Test
{
public:
template<typename Z>
void doSomething();
private:
T obj;
};
template<typename T>
template<typename Z>
void Test<T>::doSomething()
{
Z val;
std::cout << __func__ << ": type " << typeid(val).name() << std::endl;
}
int main(int argc, char *argv[])
{
Test<double> a;
a.doSomething<int>();
a.doSomething<double>();
}
icecrime posted a temporary answer and it gets compiled due to some bug probably by visual C++ 2008:
template <typename T>
class Test{
public:
template <typename Z>
void doSomething();
//rest of things
private:
T obj;
//some things
};
template <>
template <typename T>
void Test<T>::doSomething<int>(){
//do something
}
Check his current answer though.
The funny thing at least with VC++ 2008 is, no problem compiling when specializing with inline definitions, but for specializations with not-inline definitions once there's more than one version it doesn't get compiled successfully.
Related
The following code:
template <typename S, typename T>
struct foo {
void bar();
};
template <typename T>
void foo <int, T>::bar() {
}
gives me the error
invalid use of incomplete type 'struct foo<int, T>'
declaration of 'struct foo<int, T>'
(I'm using gcc.) Is my syntax for partial specialization wrong? Note that if I remove the second argument:
template <typename S>
struct foo {
void bar();
};
template <>
void foo <int>::bar() {
}
then it compiles correctly.
You can't partially specialize a function. If you wish to do so on a member function, you must partially specialize the entire template (yes, it's irritating). On a large templated class, to partially specialize a function, you would need a workaround. Perhaps a templated member struct (e.g. template <typename U = T> struct Nested) would work. Or else you can try deriving from another template that partially specializes (works if you use the this->member notation, otherwise you will encounter compiler errors).
Although coppro mentioned two solutions already and Anonymous explained the second one, it took me quite some time to understand the first one. Maybe the following code is helpful for someone stumbling across this site, which still ranks high in google, like me. The example (passing a vector/array/single element of numericalT as dataT and then accessing it via [] or directly) is of course somewhat contrived, but should illustrate how you actually can come very close to partially specializing a member function by wrapping it in a partially specialized class.
/* The following circumvents the impossible partial specialization of
a member function
actualClass<dataT,numericalT,1>::access
as well as the non-nonsensical full specialisation of the possibly
very big actualClass. */
//helper:
template <typename dataT, typename numericalT, unsigned int dataDim>
class specialised{
public:
numericalT& access(dataT& x, const unsigned int index){return x[index];}
};
//partial specialisation:
template <typename dataT, typename numericalT>
class specialised<dataT,numericalT,1>{
public:
numericalT& access(dataT& x, const unsigned int index){return x;}
};
//your actual class:
template <typename dataT, typename numericalT, unsigned int dataDim>
class actualClass{
private:
dataT x;
specialised<dataT,numericalT,dataDim> accessor;
public:
//... for(int i=0;i<dataDim;++i) ...accessor.access(x,i) ...
};
If you need to partially specialise a constructor, you might try something like:
template <class T, int N>
struct thingBase
{
//Data members and other stuff.
};
template <class T, int N> struct thing : thingBase<T, N> {};
template <class T> struct thing<T, 42> : thingBase<T, 42>
{
thing(T * param1, wchar_t * param2)
{
//Special construction if N equals 42.
}
};
Note: this was anonymised from something I'm working on. You can also use this when you have a template class with lots and lots of members and you just want to add a function.
If you're reading this question then you might like to be reminded that although you can't partially specialise methods you can add a non-templated overload, which will be called in preference to the templated function. i.e.
struct A
{
template<typename T>
bool foo(T arg) { return true; }
bool foo(int arg) { return false; }
void bar()
{
bool test = foo(7); // Returns false
}
};
In C++ 17, I use "if constexpr" to avoid specialize (and rewrite) my method. For example :
template <size_t TSize>
struct A
{
void recursiveMethod();
};
template <size_t TSize>
void A<TSize>::recursiveMethod()
{
if constexpr (TSize == 1)
{
//[...] imple without subA
}
else
{
A<TSize - 1> subA;
//[...] imple
}
}
That avoid to specialize A<1>::recursiveMethod().
You can also use this method for type like this example :
template <typename T>
struct A
{
void foo();
};
template <typename T>
void A<T>::foo()
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic" << std::endl;
}
else
{
std::cout << "other" << std::endl;
}
}
int main()
{
A<char*> a;
a.foo();
A<int> b;
b.foo();
}
output :
other
arithmetic
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;
}
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.
The following code:
template <typename S, typename T>
struct foo {
void bar();
};
template <typename T>
void foo <int, T>::bar() {
}
gives me the error
invalid use of incomplete type 'struct foo<int, T>'
declaration of 'struct foo<int, T>'
(I'm using gcc.) Is my syntax for partial specialization wrong? Note that if I remove the second argument:
template <typename S>
struct foo {
void bar();
};
template <>
void foo <int>::bar() {
}
then it compiles correctly.
You can't partially specialize a function. If you wish to do so on a member function, you must partially specialize the entire template (yes, it's irritating). On a large templated class, to partially specialize a function, you would need a workaround. Perhaps a templated member struct (e.g. template <typename U = T> struct Nested) would work. Or else you can try deriving from another template that partially specializes (works if you use the this->member notation, otherwise you will encounter compiler errors).
Although coppro mentioned two solutions already and Anonymous explained the second one, it took me quite some time to understand the first one. Maybe the following code is helpful for someone stumbling across this site, which still ranks high in google, like me. The example (passing a vector/array/single element of numericalT as dataT and then accessing it via [] or directly) is of course somewhat contrived, but should illustrate how you actually can come very close to partially specializing a member function by wrapping it in a partially specialized class.
/* The following circumvents the impossible partial specialization of
a member function
actualClass<dataT,numericalT,1>::access
as well as the non-nonsensical full specialisation of the possibly
very big actualClass. */
//helper:
template <typename dataT, typename numericalT, unsigned int dataDim>
class specialised{
public:
numericalT& access(dataT& x, const unsigned int index){return x[index];}
};
//partial specialisation:
template <typename dataT, typename numericalT>
class specialised<dataT,numericalT,1>{
public:
numericalT& access(dataT& x, const unsigned int index){return x;}
};
//your actual class:
template <typename dataT, typename numericalT, unsigned int dataDim>
class actualClass{
private:
dataT x;
specialised<dataT,numericalT,dataDim> accessor;
public:
//... for(int i=0;i<dataDim;++i) ...accessor.access(x,i) ...
};
If you need to partially specialise a constructor, you might try something like:
template <class T, int N>
struct thingBase
{
//Data members and other stuff.
};
template <class T, int N> struct thing : thingBase<T, N> {};
template <class T> struct thing<T, 42> : thingBase<T, 42>
{
thing(T * param1, wchar_t * param2)
{
//Special construction if N equals 42.
}
};
Note: this was anonymised from something I'm working on. You can also use this when you have a template class with lots and lots of members and you just want to add a function.
If you're reading this question then you might like to be reminded that although you can't partially specialise methods you can add a non-templated overload, which will be called in preference to the templated function. i.e.
struct A
{
template<typename T>
bool foo(T arg) { return true; }
bool foo(int arg) { return false; }
void bar()
{
bool test = foo(7); // Returns false
}
};
In C++ 17, I use "if constexpr" to avoid specialize (and rewrite) my method. For example :
template <size_t TSize>
struct A
{
void recursiveMethod();
};
template <size_t TSize>
void A<TSize>::recursiveMethod()
{
if constexpr (TSize == 1)
{
//[...] imple without subA
}
else
{
A<TSize - 1> subA;
//[...] imple
}
}
That avoid to specialize A<1>::recursiveMethod().
You can also use this method for type like this example :
template <typename T>
struct A
{
void foo();
};
template <typename T>
void A<T>::foo()
{
if constexpr (std::is_arithmetic_v<T>)
{
std::cout << "arithmetic" << std::endl;
}
else
{
std::cout << "other" << std::endl;
}
}
int main()
{
A<char*> a;
a.foo();
A<int> b;
b.foo();
}
output :
other
arithmetic
Can you specialize a template method within a template class without specializing the class template parameter?
Please note that the specialization is on the value of the template parameter, not its type.
This seems to compile under Visual Studio 2008 SP1 complier, but not GCC 4.2.4.
#include <iostream>
using namespace std;
template <typename T>
class A
{
private:
template <bool b>
void testme();
template <>
void testme<true>() { cout << "true" << endl; };
template <>
void testme<false>() { cout << "false" << endl; };
public:
void test();
};
template<typename T> struct select {};
template<> struct select<int> { static const bool value = true; };
template<> struct select<double> { static const bool value = false; };
template <class T>
void A<T>::test() { testme<select<T>::value>(); }
int main(int argc, const char* argv[])
{
A<int> aInt;
A<double> aDouble;
aInt.test();
aDouble.test();
return 0;
}
GCC tells me:"error: explicit specialization in non-namespace scope ‘class A’"
If it is not supported in the standard, can anyone tell me why?
It is not supported in the standard (and it is apparently a known bug with Visual Studio that you can do it).
The standard doesn't allow an inner template (member function or class) to be specialized without the outer template being specialized as well. One of the reasons for this is that you can normally just overload the function:
template<typename ty>
class A
{
public:
void foo(bool b);
void foo(int i);
};
Is equivalent to:
template<typename ty>
class A
{
public:
template<typename ty2>
void foo(ty2);
template<>
void foo(bool b);
template<>
void foo(int i);
};
here is how you do it:
template<typename A>
struct SomeTempl {
template<bool C> typename enable_if<C>::type
SomeOtherTempl() {
std::cout << "true!";
}
template<bool C> typename enable_if<!C>::type
SomeOtherTempl() {
std::cout << "false!";
}
};
You can get enable_if from my other answer where i told them how to check for a member function's existance in a class using templates. or you can use boost, but remember to change enable_if to enable_if_c then.
Here is another workaround, also useful when you need to partialy specialize a function (which is not allowed). Create a template functor class (ie. class whose sole purpose is to execute a single member function, usually named operator() ), specialize it and then call from within your template function.
I think I learned this trick from Herb Sutter, but do not remember which book (or article) was that. For your needs it is probably overkill, but nonetheless ...
template <typename T>
struct select;
template <bool B>
struct testme_helper
{
void operator()();
};
template <typename T>
class A
{
private:
template <bool B> void testme()
{
testme_helper<B>()();
}
public:
void test()
{
testme<select<T>::value>();
}
};
template<> void testme_helper<true>::operator()()
{
std::cout << "true" << std::endl;
}
template<> void testme_helper<false>::operator()()
{
std::cout << "false" << std::endl;
}
I've never heard of that being possible; it would make sense to me if it was not supported by all compilers. So here is an idea for a workaround:
Implement a template function outside of your class which takes the same action as the method. Then you can specialize this function, and it call it from the method. Of course, you'll also have to pass in any member variables that it needs (and pointers thereto if you want to modify their values).
You could also create another template class as a subclass, and specialize that one, although I've never done this myself and am not 100% sure it would work. (Please comment to augment this answer if you know whether or not this second approach would work!)