Why do template template parameters with constraints require stricter arguments?

Viewed 288

N3580 describes the following scenario:

template<Object T, template<Object> Cont>
struct stack {
    Cont<T> container;
};

template<Object>
struct my_vector;

template<Regular>
struct my_list;

template<typename>
struct my_magic;

Here, Regular is a refinement of Object; that is, every Regular is an Object but not every Object is a Regular.

I would expect the type system to be such that for stack<X, Y> to be valid, X must be an Object and Y must be instantiable with an Object. This would mean that stack<int, my_vector> and stack<int, my_magic> are valid, while stack<int, my_list> is not. Much like the case with normal functions:

struct Base {};
struct Derived : Base {};

void foo(Base* p, function<void(Base*)> fun) {
    fun(p);
}

template<typename T>
void bar(T*);

I would expect that if p is a Base*, then foo(p, bar<Base>) and foo(p, bar<void>) are valid, while foo(p, bar<Derived>) is not; after all, a Base* has an implicit conversion to a void*, but not to a Derived*.

In the case of templates, though, the situation is the opposite. Only stack<int, my_vector> and stack<int, my_list> are allowed, while stack<int, my_magic> is forbidden. Why is this? my_magic works fine with any type, while my_list may fail depending on what object I give it. Moreover, I can trivially make my_magic work with only objects:

template<Object T>
struct my_restricted_magic : my_magic<T> {};

Now my_restricted_magic can be used with stack. On the other hand, there's no easy way of making a my_list which accepts any type, but this is exactly what passing it as a template template parameter now allows.

Am I misinterpreting the purpose of constraints on template template parameter's parameters?

2 Answers
Related