N3580 описывает следующий сценарий:
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;
Здесь Regular является уточнением Object; т.е. каждый Regular является
Object, но не каждый Object является Regular.
Я бы ожидал, что система типов будет таковой, что для stack<X, Y>
X должен быть Object, а Y должен быть реалистичным с Object. Эта
будет означать, что stack<int, my_vector> и stack<int, my_magic> действительны,
а stack<int, my_list> - нет. Как и в случае с нормальными функциями:
struct Base {};
struct Derived : Base {};
void foo(Base* p, function<void(Base*)> fun) {
fun(p);
}
template<typename T>
void bar(T*);
Я бы ожидал, что если p является Base*, то foo(p, bar<Base>) и foo(p,
bar<void>) действительны, а foo(p, bar<Derived>) - нет; в конце концов,
Base* имеет неявное преобразование в void*, но не в Derived*.
Однако в случае шаблонов ситуация противоположная. Только
stack<int, my_vector> и stack<int, my_list> разрешены, а stack<int,
my_magic> запрещено. Почему это? my_magic работает отлично с любым типом,
а my_list может выйти из строя в зависимости от того, какой объект я ему даю. Более того, я могу
тривиально сделать my_magic работать только с объектами:
template<Object T>
struct my_restricted_magic : my_magic<T> {};
Теперь my_restricted_magic можно использовать с stack. С другой стороны, есть
нет простого способа сделать my_list, который принимает любой тип, но это точно
что позволяет передать его как параметр шаблона шаблона.
Я неверно истолковываю цель ограничений для параметра шаблона шаблона параметры?