Templated Template Specialization?

May 27, 2011 at 5:59pm
Is it possible to template a template specialization, eg to have 'sub' template parameters for a specific template specialization? EG, using A<int>, A<double> as regular templates, but A<string> is a template specialization that wants more information than just the fact that you want to use a string, possibly A<string><int, float>? And if it is possible, does it apply to only functions, classes, or both? I'm not worried about bad ideas or downsides, I'm just curious and want to learn...I don't actually need this for anything, but I might if I knew how to utilize it and its benefits/downsides.

EDIT: I don't mean A<B<c,d>, e>, that's completely unrelated to what I am trying to do.
Last edited on May 27, 2011 at 6:21pm
May 27, 2011 at 7:23pm
Something like this?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
template <typename FT,typename PT0=char,typename PT1=char,typename PT2=char,typename PT3=char,typename PT4=char>
struct binder{
	FT f;
	PT0 pt0;
	PT1 pt1;
	PT2 pt2;
	PT3 pt3;
	PT4 pt4;
	int p;
	bool free_after_first_use;
	typedef void(*Call_1)(PT0);
	typedef void(*Call_2)(PT0,PT1);
	typedef void(*Call_3)(PT0,PT1,PT2);
	typedef void(*Call_4)(PT0,PT1,PT2,PT3);
	typedef void(*Call_5)(PT0,PT1,PT2,PT3,PT4);
	binder():f(0),p(0),free_after_first_use(1){}
	binder(FT f,const PT0 &pt0):f(f),pt0(pt0),p(1),free_after_first_use(1){}
	binder(FT f,const PT0 &pt0,const PT1 &pt1):f(f),pt0(pt0),pt1(pt1),p(2),free_after_first_use(1){}
	binder(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2):f(f),pt0(pt0),pt1(pt1),pt2(pt2),p(3),free_after_first_use(1){}
	binder(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2,const PT3 &pt3):f(f),pt0(pt0),pt1(pt1),pt2(pt2),pt3(pt3),p(4),free_after_first_use(1){}
	binder(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2,const PT3 &pt3,const PT4 &pt4):f(f),pt0(pt0),pt1(pt1),pt2(pt2),pt3(pt3),pt4(pt4),p(5),free_after_first_use(1){}
	void call(){
		if (p<3){
			if (p<2){
				((Call_1)f)(pt0);
			}else{
				((Call_2)f)(pt0,pt1);
			}
		}else if (p>3){
			if (p<5){
				((Call_4)f)(pt0,pt1,pt2,pt3);
			}else{
				((Call_5)f)(pt0,pt1,pt2,pt3,pt4);
			}
		}else{
			((Call_3)f)(pt0,pt1,pt2);
		}
	}
};

template <typename FT,typename PT0>
inline binder<FT,PT0> *bind(FT f,const PT0 &pt0){
	return new binder<FT,PT0>(f,pt0);
}
template <typename FT,typename PT0,typename PT1>
inline binder<FT,PT0,PT1> *bind(FT f,const PT0 &pt0,const PT1 &pt1){
	return new binder<FT,PT0,PT1>(f,pt0,pt1);
}
template <typename FT,typename PT0,typename PT1,typename PT2>
inline binder<FT,PT0,PT1,PT2> *bind(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2){
	return new binder<FT,PT0,PT1,PT2>(f,pt0,pt1,pt2);
}
template <typename FT,typename PT0,typename PT1,typename PT2,typename PT3>
inline binder<FT,PT0,PT1,PT2,PT3> *bind(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2,const PT3 &pt3){
	return new binder<FT,PT0,PT1,PT2,PT3>(f,pt0,pt1,pt2,pt3);
}
template <typename FT,typename PT0,typename PT1,typename PT2,typename PT3,typename PT4>
inline binder<FT,PT0,PT1,PT2,PT3,PT4> *bind(FT f,const PT0 &pt0,const PT1 &pt1,const PT2 &pt2,const PT3 &pt3,const PT4 &pt4){
	return new binder<FT,PT0,PT1,PT2,PT3,PT4>(f,pt0,pt1,pt2,pt3,pt4);
}
#define BINDER_TYPEDEF_1(name,arg1) typedef binder<void(*)(arg1),arg1> name
#define BINDER_TYPEDEF_2(name,arg1,arg2) typedef binder<void(*)(arg1,arg2),arg1,arg2> name
#define BINDER_TYPEDEF_3(name,arg1,arg2,arg3) typedef binder<void(*)(arg1,arg2,arg3),arg1,arg2,arg3> name
#define BINDER_TYPEDEF_4(name,arg1,arg2,arg3,arg4) typedef binder<void(*)(arg1,arg2,arg3,arg4),arg1,arg2,arg3,arg4> name
#define BINDER_TYPEDEF_5(name,arg1,arg2,arg3,arg4,arg5) typedef binder<void(*)(arg1,arg2,arg3,arg4,arg5),arg1,arg2,arg3,arg4,arg5> name 
May 27, 2011 at 10:53pm
Er, no...that's what I'm trying to avoid. I mean something like this, as an example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
template<typename T>
struct Adder
{
  T var;
  Adder(const T& from) : T(from) {}
  Adder<T>& Add(const Adder<T>& with)
  {
    var += with.var;
    return(*this);
  }
};

template<unsigned long num>
template<>
struct Adder<T*>
{
  T* arr;
  Adder(const T& from[num])
  {
    for(unsigned long i; i < num; ++i)
    {
      arr[i] = from[i];
    }
  }
  Adder<T><num>& Add(const Adder<T><num>& with)
  {
    for(unsigned long i = 0; i < num; ++i)
    {
      arr[i] += with.arr[i];
    }
    return(*this);
  }
};

1
2
3
4
5
6
7
8
9
10
11
12
Adder<double> a (1.2);
Adder<double> b (3.4);
cout << a.add(b).var << endl;
int ct[5] = {1, 2, 3, 4, 5};
int dt[5] = {6, 7, 8, 9, 10};
Adder<int*><5> c (ct);
Adder<int*><5> d (dt);
c.add(d);
for(unsigned long i = 0; i < 5; ++i)
{
  cout << (i != 0 ? ", " : "") << c.arr[i];
}
4.6
7, 9, 11, 13, 15


Obviously, there are much better ways to do this. My example is very impractical, let alone bad, but my point is that I want to know if this is possible, and if it is, then how to correctly do it. I'm not looking for nifty work-arounds like above, those can be appreciated later.
Last edited on May 27, 2011 at 10:56pm
May 27, 2011 at 11:23pm
I had a similar problem while playing with a SmartArray class a while ago.

I had something like this:

1
2
template <class T, class SizeType, /*...*/ >
class SmartArray: public SizeType, /*...*/ { /*...*/ };

And the SizeType classes looked like this:

1
2
3
4
5
template <class T>
class Dynamic { /*...*/ };

template <class T, unsigned N>
class Static { /*...*/ };

And I had to instantiate my SmartArrays like this:

1
2
SmartArray <int, Dynamic<int>, /*...*/ > array1;
SmartArray <int, Static<int,10>, /*...*/ > array2;

Having to specify the type twice was ugly.
Plus, one could do dangerous things, like:

SmartArray <double, Dynamic<int>, /*...*/ > array;

That's how I solved it:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
struct Dynamic 
{
    template <class T>
    class SizeType { /*...*/ };
};

template <unsigned N>
struct Static 
{
    template <class T>
    class SizeType { /*...*/ };
};

//...

template <class T, template<class> class SizeType, /*...*/ >
class SmartArray: public SizeType<T>, /*...*/ { /*...*/ };

//...

SmartArray <int, Dynamic::SizeType, /*...*/ > array1;
SmartArray <int, Static<10>::SizeType, /*...*/ > array2;

In short, one solution to your problem is to use nested classes.

EDIT: Ok, I think I'm done editing for now.
Last edited on May 27, 2011 at 11:43pm
May 28, 2011 at 12:21am
Ok, but...as I said, I'm not looking for a solution or work around. I want to know plain out if you can nest templates like I have tried to do above. In other words, is it possible to do this in the first place:
1
2
3
template<typename T>
template<typename N>
class Hello { T t[N]; };
because if it is, then that means I should be able to do this:
1
2
3
4
5
6
template<typename T>
class Hello { T t; };

template<unsigned long N>
template<>
class Hello<char*> { char t[N]; }


This is just a simple "can I " + something + ", and if so how?" question. All I want to do is template a specialization of another template. Since all this sort of stuff is handled by the compiler at compile time...I don't see why not :p
Last edited on May 28, 2011 at 12:22am
May 28, 2011 at 2:15am
1
2
3
template<typename T>
template<typename N>
class Hello { T t[N]; };
Not like this. A single {class|function} declaration can be preceded by just one template parameter list. This would have to be rewritten as
1
2
template<typename T,unsigned N>
class Hello { T t[N]; };
If you want so specialize T and N separately, I believe that's what partial specialization is for. I don't remember the syntax, but you can look it up.

A few off-topic notes:
* You can't have a typename as the size of an array. You also can't "templetize" the type of a template parameter (at least not AFAIK).
* The type of Hello<char *,/*...*/>::t would be char *[], not char [].
May 28, 2011 at 2:18am
Thanks and thanks, I'm just trying to get across a rough concept, and I do see and understand my mistakes. I'll look up partial specialization.
May 28, 2011 at 7:04am
Well, partial template specialization wouldn't work well for every case.

Sure, it's ok if you just want something like this:

1
2
3
4
5
template <class T, unsigned N=1>
class MyClass { /*...*/ };

template<class T, unsigned N>
class MyClass<T*, N> { /*...*/ };

And if this is what you want, then by all means go for it.

But this assumes that every specialization you'll make
requires an integer as additional data. What if you want
to provide a bool if T is a string and a char if T is a double?
In that case, you'll have to resort to something else.

helios wrote:
You also can't "templetize" the type of a template parameter

What do you mean?

Last edited on May 28, 2011 at 7:24am
May 28, 2011 at 2:55pm
This:
template<typename T>
template<T N>
May 29, 2011 at 6:00pm
I see that partial specialization is not what I want...I want something like this:
1
2
3
4
5
template<typename T>
class MyClass{};

template<typename T, int N>
class MyClass<bool, N>{};


In that, if I were to use MyClass<int> it would use the first class definition, and if I were to use MyClass<bool, 5> I have to provide the extra parameter and it would use the second class definition, and MyClass<int, 5> would not compile (as it would for M4ster R0shi's case)

Basically...since I can't template template specializations, can I overload templates similar to function overloads? All these things I am asking seem to not be directly possible even though they could just as easily be a part of the C++ language :\
May 29, 2011 at 11:03pm
You could:

1
2
3
4
5
6
7
8
9
template< typename T, size_t N >
class MyClass;

template< size_t N >
class MyClass< bool, N >
{};

template<>
class MyClass<int, 0> {};


But you would have to specialize the third template for every type.

Trouble is that the template specialization mechanism allows you to reduce the number of parameters, not increase it.

Another thought:
1
2
3
4
5
6
7
8
template< size_t N >
struct Bool {};

template< typename T >
class MyClass {};

template< >
class MyClass<bool>; // Leave unimplemented to force compile error if template instantiated with raw bool instead of Bool<>. 


You may need to specialize for Bool<N>.
Topic archived. No new replies allowed.