Why does std::stack not use template template parameter?

Because typically containers like std::vector have more than one template argument. By not caring about it being a template, you allow every kind of container to be used.

How would

template<class T, class Allocator = std::allocator<T>> class vector;

fit onto

template<typename> class Container

as you would have it in your stack? (Hint: it doesn't!) You'd need special cases for each number and kind of template arguments (type vs. non-type) you'd want to support, which is silly, because these typically don't contribute any more information than a simple

typename Container

Note that to get at the actual template arguments of e.g. a std::vector, you have the typedefs std::vector::value_type and std::vector::allocator_type, removing the need of having these types available explicitly where you actually use the type (i.e. the Container of stack).


In short: Because using a template template parameter is more restrictive* than using a type parameter without providing any advantages.

* By restrictive I mean that you may need a more complex stuff to obtain the same results than with a "simple" type parameter.

Why is there no advantages?

Your std::stack probably has an attribute like this:

template <typename T, typename Container>
struct stack {
    Container container;
};

If you replace Container, by a template template parameter, why would you obtain?

template <typename T, template <typename...> class Container>
struct stack {
    Container<T> container;
};

You are instantiating Container only once and only for T (Container<T>), so there is no advantages for a template template parameter.

Why is it more restrictive?

With a template template parameter, you have to pass to std::stack a template which expose the same signature, e.g.:

template <typename T, template <typename> class Container>
struct stack;

stack<int, std::vector> // Error: std::vector takes two template arguments

Maybe you could use variadic templates:

template <typename T, template <typename... > class Container>
struct stack {
    Container<T> container;
};

stack<int, std::vector> // Ok, will use std::vector<int, std::allocator<int>>

But what if I do not want to use the standard std::allocator<int>?

template <typename T, 
          template <typename....> class Container = std::vector, 
          typename Allocator = std::allocator<T>>
struct stack {
    Container<T, Allocator> container;
};

stack<int, std::vector, MyAllocator> // Ok...

This is becoming a bit messy... What if I want to use my own container templates that takes 3/4/N parameters?

template <typename T,
          template <typename... > class Container = std::vector,
          typename... Args>
struct stack {
    Container<T, Args...> container;
};

stack<int, MyTemplate, MyParam1, MyParam2> // Ok...

But, what if I want to use a non-templated containers?

struct foo { };
struct foo_container{ };

stack<foo, foo_container> // Error!

template <typename... >
using foo_container_template = foo_container;

stack<foo, foo_container_template> // Ok...

With a type parameter there are no such issues1:

stack<int>
stack<int, std::vector<int, MyAllocator<int>>
stack<int, MyTemplate<int, MyParam1, MyParam2>>
stack<foo, foo_container>

1 There are other cases which do not work with template template parameter such as using templates accepting a mix of type and non-type parameters in specific orders, for which you can create generic template template parameter, even using variadic templates.


Using a template template parameter would restrict the types that you could use as the underlying container to those that expose the same template signature. This form allows arbitrary types as long as they support the expected interface.

Tags:

C++

Templates