Как выполняется вывод аргумента шаблона для параметра шаблона функции, если это шаблон класса с аргументом по умолчанию - PullRequest
4 голосов
/ 13 июля 2020
template<typename T, typename U = T>
struct Test{};

template<typename T>
void func(Test<T>){  //#1
}

int main(){
  func(Test<int>{});  //#2
}

Обратите внимание на приведенный выше код. В момент вызова шаблона функции func тип аргумента равен Test<int,int>. При вызове шаблона функции выполняется вывод аргументов шаблона.

Правило вывода аргументов шаблона для вызова функции: temp.deduct # call-1

Вывод аргументов шаблона выполняется путем сравнения каждого типа параметра шаблона функции (назовите его P), который содержит параметры шаблона, которые участвуют в выводе аргументов шаблона с типом соответствующего аргумента вызова (назовите его A), как описано ниже.

Я почти уверен, что тип A - Test<int,int>, однако я не уверен что это за тип P. Это Test<T> или Test<T,T>. Согласно правилу, тип P здесь Test<T>, затем выполняется процесс вычитания, чтобы определить значение T, которое участвует в выводе аргумента шаблона. Затем в соответствии с этими правилами, описанными следующим образом:

temp.deduct # call-4

В общем, процесс вывода пытается найти значения аргументов шаблона что сделает выведенный A идентичным A (после преобразования типа A, как описано выше).

temp.deduct # 5

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

Поскольку шаблон класса Test имеет аргумент по умолчанию, выводимый T подставляется в аргумент по умолчанию. Это означает, что выведенное A равно Test<int,int> и идентично типу аргумента Test<int,int>.

Однако это просто мое понимание. Я не уверен, что это за тип P. Если изменить тип аргумента функции на Test<int,double>, результат будет сообщать:

candidate template ignored: deduced conflicting types for parameter 'T' ('int' vs. 'double')

Результат выглядит так, как если бы P было Test<T,T> и первое значение of T конфликтует со вторым значением T.

Итак, мой вопрос:

Является ли P здесь Test<T> или Test<T,T>? а почему?

Ответы [ 4 ]

1 голос
/ 18 июля 2020

не языковый ответ юриста

Типа не существует Test<T> на самом деле является сокращением для Test<T, T>.

Так же, как с аргументами функции по умолчанию если у вас есть int foo(int a, int b = 24), тип функции - int (int, int), а любой вызов вроде foo(11) на самом деле foo(11, 24).

0 голосов
/ 21 июля 2020

Является ли 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]:

При определенных точек в процессе вывода аргументов шаблона, это необходимо Попробуйте взять тип функции, который использует параметры шаблона, и заменить эти параметры шаблона соответствующими аргументами шаблона. Это делается в начале вывода аргументов шаблона , когда любые явно указанные аргументы шаблона подставляются в тип функции, и снова в конце вывода аргументов шаблона , когда любые аргументы шаблона, которые были выводимые или полученные из аргументов по умолчанию заменяются.

, а именно: процесс вывода аргументов шаблона разделен на четкие начало и конец , классифицируя:

  • явно указаны аргументы шаблона как начало процесса и,
  • выведенные или полученные аргументом аргументы шаблона по умолчанию как конец процесса,

который объяснил бы различия в сообщениях об ошибках в приведенных выше примерах; если все аргументы шаблона были явно указаны в начале процесса вывода, у остальной части процесса не будет оставшихся аргументов шаблона для работы с выводом или аргументами шаблона по умолчанию.

0 голосов
/ 18 июля 2020

P должен быть типом, а не шаблоном. test <T> - это идентификатор шаблона, но в стандарте явно не сказано, что идентификатор шаблона test <T> эквивалентен test<T,T>. Единственное, что сказано:

Идентификатор шаблона действителен, если

  • [...]
  • есть аргумент для каждого невыводимого параметра non-pack, который не имеет аргумента шаблона по умолчанию, [...]

После этого пробелы в стандарте заполняются нашей интуицией, ориентированной на использование термина по умолчанию .

Я думаю, что ключевым моментом здесь является то, что шаблон обозначает семейство, и идентификатор-шаблона не может обозначать семейство.

0 голосов
/ 18 июля 2020

Я попытался придумать код, который вынуждает только вывод классов без вывода функций. Здесь нет экземпляров функций, но компилятор все равно выдает ошибку:

template<typename T, typename U = T>
struct Test{};

template<typename T> 
void func(Test<T, T>){
}

template<typename T>
void func(Test<T>){  
}

redefinition of 'template<class T> void func(Test<T, T>)'

G CC: https://godbolt.org/z/7c981E Clang: https://godbolt.org/z/G1eKTx

Предыдущий неправильный ответ:

P относится к параметру шаблона, а не к самому шаблону. В декларации Test<typename T, typename U = T> P относится к T, а не к Test. Таким образом, в экземпляре Test<int> T - это int, как и A в вызове - также int.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...