Почему параметры шаблона шаблона с ограничениями требуют более строгих аргументов?

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, который принимает любой тип, но это точно что позволяет передать его как параметр шаблона шаблона.

Я неверно истолковываю цель ограничений для параметра шаблона шаблона параметры?

Ответ 1

Это ошибка в предложении. Ограниченный шаблонный шаблонный параметр должен принимать аргументы с более слабыми ограничениями.

Ответ 2

Это гипотеза, но это похоже на вероятное объяснение:

Предоставление более конкретного шаблона, а не более общего, - это способ, которым в настоящее время действуют правила в отношении параметров шаблона шаблона. Вы можете передать шаблон однотипного параметра, где ожидается переменная:

template<template<typename...> class> struct Foo {};
template<typename> Bar {};

Foo<Bar>(); // legal

Но не наоборот:

template<template<typename> class> struct Foo {};
template<typename...> Bar {};

Foo<Bar>(); // error, argument/parameter mismatch.

Фразы, возникшие в N2555, где было предложено разрешить такой код:

template<typename>
struct Foo;

template<template<typename...> class Fun, template... Args>
struct Foo<Fun<Args...>> {};

Foo<std::pair<int, double>>();

В принципе, вместо template<typename...> class, являющегося гарантией для пользователя, это все, что нужно, чтобы пользователь должен был дать разумные аргументы. Учитывая это использование, связанное с специализациями, это кажется разумным.

Это не объясняет, почему использование более общего шаблона не допускается, но изменение фраз в N3580 сделало бы два правила составленными довольно неинтуитивными.

Ссылка на предложение: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2555.pdf