Является ли P
здесь Test<T>
или Test<T,T>
? и почему?
P
это Test<T,T>
.
Думаю, мы можем согласиться с тем, что правила [temp.deduct] применимы также к шаблонам классов; например, [temp.class.order] , охватывающий частичное упорядочение специализаций шаблонов классов, полностью основан на концепции переписывания шаблонов классов в (изобретенные) шаблоны функций и применения правил шаблонов функций к то, что из изобретенных шаблонов функций соответствует исходным шаблонам классов при анализе частичного упорядочения. В сочетании с тем фактом, что стандартный отрывок для шаблонов классов довольно краток по сравнению с шаблонами функций, я интерпретирую приведенные ниже ссылки как применимые также и для шаблонов классов.
Теперь из [temp.deduct] / 1 [ выделение мое]:
Когда имеется ссылка на специализацию шаблона функции , , все аргументы шаблона должны иметь значения . Значения могут быть указаны явно или , в некоторых случаях, могут быть выведены из использования или получены из аргументов шаблона по умолчанию. [...]
и, начиная с [temp.deduct] / 2 [ focus mine]:
Когда указан явный список аргументов шаблона, аргументы шаблона должны быть совместимы со списком параметров шаблона и должны приводить к допустимому типу функции, как описано ниже; иначе определение типа не удастся. В частности, при оценке явно указанного списка аргументов шаблона по отношению к заданному шаблону функции выполняются следующие шаги:
- (2.1) Указанные аргументы шаблона должны соответствовать параметрам шаблона натурой (т. е. типом, не типом, шаблоном). Не должно быть аргументов больше, чем параметров, если [...]
С дополнительным акцентом на " указывается "и " указанные аргументы шаблона"; не требуется указывать все аргументы для данного шаблона функции сопоставления (/ class), только те, которые указывают , соответствуют требованиям [temp.deduct] / 2 для явно указанные аргументы шаблона.
Это приводит нас к [temp.deduct] / 1 для оставшихся аргументов шаблона данного кандидата функции / шаблона класса: они могут быть либо выведены ( шаблоны функций) или полученные из аргументов шаблона по умолчанию . Таким образом, вызов:
func(Test<int>{});
, согласно приведенному выше аргументу, семантически эквивалентен
func(Test<int, int>{});
с основным отличием, что аргументы шаблона для первый определяется как явно указанными аргументами шаблона, так и аргументом шаблона по умолчанию, тогда как для последнего оба определяются явно указанными аргументами шаблона. Отсюда ясно, что A
равно Test<int, int>
, но мы будем использовать аналогичный аргумент для P
.
Из [temp.deduct.type] / 3 [ акцент мой]:
Данный тип P
может быть составлен из количество других типов, шаблоны и значения, не являющиеся типами:
- [...]
- (3.3) Тип , который является специализация шаблона класса (например,
A<int>
) включает типы , шаблоны и значения, не являющиеся типами , на которые ссылается список аргументов шаблона для специализации .
Обратите внимание, что описание в [temp.deduct.type] /3.3 теперь возвращается в шаблон список аргументов типа шаблона P
. Не имеет значения, что P
, поскольку при проверке этой конкретной функции-кандидата в разрешении перегрузки, ссылается на шаблон класса, частично явно указывая список аргументов шаблона и частично полагаясь на параметр шаблона по умолчанию, где последний зависит от экземпляра . Этот шаг разрешения перегрузки не подразумевает какого-либо экземпляра, а только проверку кандидатов. Таким образом, те же правила, которые мы только что применили к аргументу шаблона A
выше, применимы к P
, в этом случае, и поскольку ссылка на Test<int, int>
(через Test<int>
), P
это Test<int, int>
, и у нас есть идеальное совпадение для P
и A
(для пары один параметр-аргумент P
и A
в этом примере)
Сообщения об ошибках компилятора?
Исходя из приведенного выше аргумента, можно было бы ожидать аналогичного сообщения об ошибке для примера с ошибкой OP:
// (Ex1)
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func(Test<int, double>{});
}
как для следующий простой:
// (Ex2)
struct Foo {};
template<typename T> struct Test {};
template<typename T> void f(T) {}
int main() {
f<Test<int>>(Test<Foo>{});
}
Однако это не так, поскольку первое выдает следующие сообщения об ошибках для G CC и Clang соответственно:
// (Ex1)
// GCC
error: no matching function for call to 'func(Test<int, double>)'
note: template argument deduction/substitution failed:
deduced conflicting types for parameter 'T' ('int' and 'double')
// Clang
error: no matching function for call to 'func'
note: candidate template ignored: deduced
conflicting types for parameter 'T' ('int' vs. 'double')
, тогда как последнее выдает следующие сообщения об ошибках для G CC и Clang, соответственно:
// (Ex2)
// GCC
error: could not convert 'Test<Foo>{}' from 'Test<Foo>' to 'Test<int>'
// Clang
error: no matching function for call to 'f'
note: candidate function template not viable:
no known conversion from 'Test<Foo>' to 'Test<int>' for 1st argument
Мы, наконец, можем отметить, что если мы настроим (Ex1)
в При явном указании аргумента шаблона single для f
и G CC, и Clang выдают аналогичные сообщения об ошибках, как и для (Ex2)
, намекая, что вывод аргумента полностью удален из уравнения.
template<typename T, typename U = T>
struct Test{};
template<typename T>
void func(Test<T>) {}
int main() {
func<int>(Test<int, double>{});
}
Ключ для этой разницы может быть таким, как указано в [temp.deduct] / 6 [ focus mine]:
При определенных точек в процессе вывода аргументов шаблона, это необходимо Попробуйте взять тип функции, который использует параметры шаблона, и заменить эти параметры шаблона соответствующими аргументами шаблона. Это делается в начале вывода аргументов шаблона , когда любые явно указанные аргументы шаблона подставляются в тип функции, и снова в конце вывода аргументов шаблона , когда любые аргументы шаблона, которые были выводимые или полученные из аргументов по умолчанию заменяются.
, а именно: процесс вывода аргументов шаблона разделен на четкие начало и конец , классифицируя:
- явно указаны аргументы шаблона как начало процесса и,
- выведенные или полученные аргументом аргументы шаблона по умолчанию как конец процесса,
который объяснил бы различия в сообщениях об ошибках в приведенных выше примерах; если все аргументы шаблона были явно указаны в начале процесса вывода, у остальной части процесса не будет оставшихся аргументов шаблона для работы с выводом или аргументами шаблона по умолчанию.