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?
It's a bug in the proposal. A constrained template template parameter should accept arguments with weaker constraints.
This is conjecture, but it seems like a likely explanation:
Allowing to pass a more specific template, rather than a more generic one, is the way the rules currently work in regard to variadic template template parameters. You are allowed to pass a single-type-parameter template where a variadic is expected:
template<template<typename...> class> struct Foo {};
template<typename> Bar {};
Foo<Bar>(); // legal
But not vice-versa:
template<template<typename> class> struct Foo {};
template<typename...> Bar {};
Foo<Bar>(); // error, argument/parameter mismatch.
The phrasing originated in N2555 where it was requested to allow code like this:
template<typename>
struct Foo;
template<template<typename...> class Fun, template... Args>
struct Foo<Fun<Args...>> {};
Foo<std::pair<int, double>>();
Basically, instead of the template<typename...> class
being a guarantee to the user, it is a catch-all thing that the user must then provide sensible arguments for. Given this usage involving specialisations, that does seem reasonable.
This does not explain why passing a more general template is not allowed, but reversing the phrasing in N3580 would make the two rules put together rather unintuitive.
Proposal link: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With