简体   繁体   中英

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

Why do std::stack and std::queue use type template parameter instead of template template parameter for their underlying container type?

ie why is stack declared like this:

template<typename T, typename Container = deque<T>>
class stack;

but not like this:

template<typename T, template<typename> class Container = deque>
class stack;

?

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 eg 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 (ie 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, eg:

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 issues 1 :

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.

Because it doesn't compile :

std::deque isn't of type

template <typename T> class std::deque

it's of type

template<class T, class Alloc> class std::deque

This of course is a more general problem: even if we were to provide the Alloc template parameter to our stack class template, the class would now only work with containers that have exactly two type template arguments. This is an unreasonable restriction.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM