48

It seems understanding template template parameters will kill me :(, Let me explain what misconception I made in my mind which confuses me:

template<class T>
class B {}; // A templated class

Here is other code:

template<template<class X> class Z = B> // The problem is in this line for me
class BB{};

Note the line in the parameter list of templated class BB, which is:

template<class X> class Z = B

Now, what stops C++ to think that Z is not another templated class Z?

I.e.,

template<class X> class Z {
}

rather than thinking class Z is a templated parameter itself.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
M3taSpl0it
  • 2,967
  • 6
  • 28
  • 27
  • 6
    I don't understand what you're asking. C++ knows that it is a template parameters because it is inside the template parameter list. Is that what you are asking? – Peter Alexander Jun 26 '11 at 14:20
  • @Peter So can please tell me that , is template template template parameter also possible?, if yes , please help me visualize it, thanks –  Jun 26 '11 at 16:58
  • 3
    No, it's not possible. However, the reason it's not possible is simply because the standardisation committee decided that two's enough. There's nothing "conceptually" impossible about 3 levels of template indirection, it's just not standard C++. – Peter Alexander Jun 26 '11 at 17:02
  • This is the subject of [this meta question](https://meta.stackoverflow.com/questions/402257) (syntax highlighting). – Peter Mortensen Oct 23 '20 at 21:26
  • The meta question has been deleted now (but it is visible for users with more than 10,000 reputation points). – Peter Mortensen Dec 14 '20 at 14:03

3 Answers3

83

Mankarse has answered your question, but I thought I'd chime in anyway.

Template template parameters are just like normal template type parameters, except that they match templates instead of concrete types:

// Simple template class
template <typename Type>
class Foo
{
    Type m_member;
};

// Template template class
template <template <typename Type> class TemplateType>
class Bar
{
    TemplateType<int> m_ints;
};

If it helps, you can kind of think of them as like function pointers. Normal functions just accept arguments like normal templates just accept types. However, some functions accept function pointers which accept arguments, just like template template types accept templates that accept types:

void foo(int x)
{
    cout << x << endl;
}

void bar(void (*f)(int))
{
    f(1);
    f(2);
}

To answer your question in the comments: template template template parameters are not possible. However, the reason they are not possible is just because the standardisation committee decided that template templates were enough, probably to make lives easier for the compiler implementors. That being said, there's nothing stopping the committee from deciding that they are possible, then things like this would be valid C++:

template <template <template <typename> class> class TemplateTemplateType>
class Baz
{
    TemplateTemplateType<Foo> m_foos;
};

typedef Baz<Bar> Example;
// Example would then have Bar<Foo> m_foos;
// which would have Foo<int> m_ints;

Again, you can see parallels in function pointers.

                      types <=> values
                  templates <=> functions of values
         template templates <=> functions of functions of values
template template templates <=> functions of functions of functions of values

The analogous function to Baz would be:

void baz(void (*g)(void (*f)(int)))
{
    g(foo);
}

Where would you use a template template template?

It's pretty far-fetched but I can think of one example: a really generic graph searching library.

Two common algorithms in graph searching are the depth-first search (DFS) and the breadth-first search (BFS). The implementation of the two algorithms is identical except in one regard: DFS uses a stack of nodes whereas BFS uses a queue. Ideally, we'd just write the algorithm once, with the stack/queue as an argument. Also, we'd want to specify the implementation container of the stack or queue, so that we could do something like:

search<Stack, Vector>( myGraph ); // DFS
search<Queue, Deque>( myGraph ); // BFS

But what is a Stack or a Queue? Well, just like in the STL a stack or a queue can be implemented with any kind of container: vectors, deques, lists etc. and could also be stacks of any element type, so our stacks or queues would have the interface:

Stack<Vector, int> // stack of ints, using a vector implementation
Queue<Deque, bool> // queue of bools, using a deque implementation

But Vector and Deque themselves are template types!

So finally, our Stack would be a template template like:

template <template <typename> class Storage, typename Element>
struct Stack
{
    void push(const Element& e) { m_storage.push_back(e); }
    void pop() { m_storage.pop_back(); }
    Storage<Element> m_storage;
};

And our search algorithm would then have to be a template template template!

template <template <template <typename> class, typename> class DataStructure,
          template <typename> class Storage,
          typename Graph>
void search(const Graph& g)
{
    DataStructure<Storage, typename Graph::Node> data;
    // do algorithm
}

That would be pretty intense, but hopefully you get the idea.

Remember: template template templates are not legal C++, so this whole graph search thing won't actually compile. It's just a "what if?" :)

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Peter Alexander
  • 53,344
  • 14
  • 119
  • 168
  • @Peter : ThankXXXxxxx.... in tonsss... for your effort . can you please tell me more about your third line or how compiler does it? "except that they match templates instead of concrete types:" . Thanks again :) . P.S : my lord how do you know this too much in deep? , any private sources? –  Jun 26 '11 at 17:51
  • Well, if you have a normal template like vector or list, you just give it a type, e.g. vector or list. With template templates, instead of giving it a type, you give it a template. So if you had a template template TT you could do TT or TT. Note that I'm giving it a template instead of a concrete type like int or bool. That's what a template template is. – Peter Alexander Jun 26 '11 at 17:52
  • @M3taSpl0it: Templates are a huge topic in C++. Way more than I can write about in a day. I'd recommend you start here: http://www.parashift.com/c++-faq-lite/templates.html then once you feel you understand templates well maybe come back and read this again. – Peter Alexander Jun 26 '11 at 18:10
  • @Peter: I understood , everything now . one more thing to say . You're awesome , not for the above link etc . but the way you explained the answer and helped me , i really appreciate from deep heart. Thanks again:) . P.S : how can i add you to buddy list in SO ? o_0 –  Jun 26 '11 at 18:24
  • @M3taSpl0it: You're welcome :-) Unless I'm mistaken, there is no buddy list on SO. – Peter Alexander Jun 26 '11 at 18:32
  • @Peter : hya , i think there is some errata in your Search Algorithm Graph code if i understood everything , i.e `DataStructure data; \\ shouldn't storage be replaced by Stack?` –  Jun 27 '11 at 11:24
  • @M3taSpl0it: No. The template doesn't know about `Stack` -- its parameters are `DataStructure`, `Storage`, and `Graph`. People may instantiate it with `search` which would cause `DataStructure` to be replaced with `Stack`, but you don't ever write `Stack` in the template definition. – Peter Alexander Jun 27 '11 at 11:27
  • 1
    Today your `Baz Example` compiles with gcc 4.9 even in pedantic std=c++98 mode. Scary enough! – Artem Pelenitsyn Aug 11 '15 at 14:53
  • 1
    Even more: as I read a grammar for template parameters in C++98 standard (chap 14 par. 1 and section 14.1 par. 1), I figured out that ttt-parameters are perfectly allowed by the standard. – Artem Pelenitsyn Aug 11 '15 at 15:02
  • Yes, piecing together the initial Foo/Bar templates with the template-template-template later on, this compiles and instantiates without complaints: http://coliru.stacked-crooked.com/a/5c1e9df352761be3 Who has misinterpreted the standard here? ;-) – underscore_d Jan 12 '16 at 09:52
  • template-template-template-template-template seem fine too http://coliru.stacked-crooked.com/a/07e2f2e19bcef1f2 – Richard Kenneth Niescior Jun 16 '16 at 16:48
  • Hm... I interpreted the question as *'How do I specify a default for template template parameters?'* (note: *'`template – Aconcagua May 19 '23 at 15:07
18

This is part of the syntax of the language (which is monstrous and massively context-dependent). If template<class X> class Z occurs in a template-parameter-list then it is interpreted as declaration of a formal parameter Z with the kind (like a meta-type; kinds classify types in the same way types classify values) "template class taking one class argument".

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Mankarse
  • 39,818
  • 11
  • 97
  • 141
  • Hm... I interpreted the question as *'How do I specify a default for template template parameters?'* (note: *'`template – Aconcagua May 19 '23 at 15:07
2

The usage examples in the accepted answer are misleading,

especially for beginners. Granted it's hard to come up with anything that won't be contrived, but we should at least contrive something that doesn't contradict the overall principles. Template parameters should be used only when the user of our interface can't specify the type of the template for one or the other reason, and we need to do it for them. In the Stack example we ask for both Storage and Element, only to instantiate Storage with that very Element, which is entirely unnecessary, the user can easily perform a basic substitution:

Stack<deque<int>> my_stack;

And all the stack needs to do is this:

template <typename Storage>
struct Stack
{
    void push(typename Storage::const_reference e) { m_storage.push_back(e); }
    void pop() { m_storage.pop_back(); }
    Storage m_storage;
    typename Storage::reference top() { return m_storage.back(); }
};

It doesn't in any way decide for the user what the element type is, so it does not need the template parameter. Hence the search becomes

template <template <typename> class DataStructure,
      template <typename> class Storage,
      typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
    DataStructure<Storage<typename Graph::Node>> data;
    // do algorithm
}

Here I guess we assume that internal Graph::Node type is not accessible to the user, and search is somehow a friend function of the Graph, which seems to make some sense. However, do we actually need to fill the structure with graph nodes, or simply references to them? Can the user not refer to the nodes in any way? If not, why is it called a graph, and not, say, slow_unordered_set? So lets imagine for a second they have an access to some node reference/pointer type, then they can do this:

search<Stack<vector<Graph::node_ptr>>>(graph, 10);

The function simplifies further to this:

template <typename StackStructure, typename Graph>
void search(const Graph& g, typename Graph::const_reference)
{
    StackStructure data;
    // do algorithm
}

Gosh darn it, now it's more generic than ever! Do you want to specify an allocator for the storage? No problem, just do it. You instead wanted some statically allocated vector that requires maximum size parameter? Go right ahead. Want to implement the stack from scratch altogether? Well, as long as it quacks like a stack...

Perhaps a more appropriate example

of a template with template parameters would be some class that represents a complex system and uses some Storage template for a bunch of internal structures, and for some reason is parameterized on that Storage template:

template <template <typename> class Storage>
class System
{
    Storage<Component_1> components_1;
    Storage<Component_2> components_2;
    Storage<Component_3> components_3;        
    Storage<MetaInfo> registry;
    public:
    // some inane interface
};

If you ask me - this code reeks, but it's not like I wouldn't write it.

Now that we have this semi-appropriate example for a template with a template parameter, we can contrive something for a template with a template parameter that itself has a template parameter: Imagine somehow we end up with like 10 of these System classes that all have the same interface, all parameterized on a Storage template, but otherwise very VERY different. Brace yourselves for the SuperSystem, an even more complicated class, that uses a bunch of our systems, but CRUCIALLY needs to decide itself what Storage templates to use with each system.

template< template< template <typename> class Storage> class System>
class SuperSystem
{
    System<Vector> system_1;
    System<OtherVector> system_2;
    System<List> system_3;
    public:
    // absolutely bonkers interface
};

We want to specify something down the template hierarchy we're dealing with here, but still leave something up the hierarchy customizable. For some reason we don't know what exact system we will be dealing with, but we know something very specific about all of them, that we absolutely need to go our way. This is an overarching theme with these examples, our goal is not to make things more generic and customizable, but the opposite - we want to lock down certain deeply embedded things.

TL;DR

In my experience you would only encounter good use cases for templates with template parameters when knee deep in a meta programming library. A rule of thumb: if you can recognize this pattern

template<...> struct f { typedef ... type; };

as a type function, then in that mindset you are allowed to use templates with template parameters, and maybe ponder about anything deeper. Otherwise slap yourself on the wrist.

namark
  • 143
  • 6