Может ли С++ 17 вывести шаблоны шаблонов шаблонов `auto` не-типа` template` с явными параметрами не-типа?

Рассмотрим этот пример (также доступный в wandbox):

template <template <auto> class>
void test() { }

template <int> 
struct X { };

Попытка создания экземпляра test<X>() на clang++ 4.0 (trunk) приводит к ошибке компиляции:

error: no matching function for call to 'test'
     test<X>();
     ^~~~~~~

note: candidate template ignored: 
      invalid explicitly-specified argument for 1st template parameter
void test() { }

Мое первоначальное предположение/интуиция заключалось в том, что test можно использовать для соответствия любому template, имеющему непиковый параметр.


Однако, следующий фрагмент кода успешно компилируется:

template <template <auto> class>
void test() { }

//        vvvv
template <auto> 
struct X { };

Это предназначено? Не удалось найти ничего убедительного в P0127R2.

Ответ 1

Это определенно предназначено. Параметры шаблона шаблона могут соответствовать шаблонам, которые принимают одни и те же аргументы. Это:

template <template <auto> class>
void test() { }

может быть создан только с шаблоном класса, который может принимать любой тип непигового параметра. Но это:

template <int> 
struct X { };

не является таким шаблоном класса. X может быть создан только с помощью int. Это просто не соответствует спецификации шаблона шаблона, следовательно, ошибка. Что делать, если test хотел создать экземпляр своего шаблона класса с типом указателя? Или указатель на функцию или указатель на член? Это было бы невозможно.

Вторая попытка с template <auto> struct X { }; соответствует параметру шаблона-шаблона, следовательно, корректна. Также обратите внимание, что обратное, имеющее test принимает параметр template <int> class, а передача в template <auto> struct X { }; также хорошо сформирована, поскольку аргумент является более общим, чем параметр.


Соответствующая формулировка находится в [temp.arg.template]:

Аргумент шаблона соответствует шаблону-шаблону шаблона P, когда каждый из параметров шаблона в шаблоне-параметре-списке шаблонов-аргументов соответствует шаблону соответствующего шаблона или шаблону алиаса A соответствует соответствующий параметр шаблона в списке параметров шаблона P. Два параметра шаблона совпадают, если они имеют один и тот же вид (тип, не тип, шаблон), для шаблонов-шаблонов непигового типа, их типы эквивалент (14.5.6.1), а для шаблонных шаблонов-параметров каждый из их соответствующих параметров шаблона соответствует рекурсивно.


Примечание: формулировка эквивалентности принимает случай auto - auto и отклоняет случай auto - int, но также, похоже, отклоняет случай int - auto (на основе моего чтения), Я попытаюсь получить разъяснения.

Ответ 2

В дополнение к ответу Барри, в котором мне было любопытно, вот четыре возможные комбинации и результаты с использованием Clang 4.0 (SVN), см. также в wandbox:

template <bool> struct obj_bool { };  // object taking a value of boolean type
template <auto> struct obj_auto { };  // object taking a value of deduced type
//       ^^^^^^ Note: this is a template value argument (non-type template argument)

template <template <auto> typename> void fn_auto() { }
template <template <bool> typename> void fn_bool() { }
//        ^^^^^^^^^^^^^^^^^^^^^^^^ Note: this is a template type argument
//                 ^^^^^^                taking a template value argument

int main() {     
    fn_bool<obj_bool>();    // #1 bool->bool OK (exact match)
    fn_auto<obj_auto>();    // #2 auto->auto OK (exact match)
    fn_bool<obj_auto>();    // #3 bool->auto OK (sub-set)
    //fn_auto<obj_bool>();  // #4 auto->bool Error: no matching function.
}

Из этого, # 1 и # 2, очевидно, являются точными совпадениями и работают как ожидалось. # 3 будет ссылаться на реализацию bool на шаблоне, который может обрабатывать не только bool, но и все типы, тогда как # 4 будет пытаться вызвать определение, ожидающее обобщенного объекта (авто) с объектом, предоставляющим только подмножество (bool) возможностей.

Шаблонная функция fn_auto promises возможные экземпляры для шаблонов, принимающих любой тип значения (авто). Таким образом, предоставление этого только подмножества возможностей (bool) нарушает это обещание.

Хотя это и не сразу очевидно, ограничение имеет смысл. И извините за то, что моя формулировка не соответствует стандарту С++.