Перегрузка функции: пустой список параметров против пакета параметров - PullRequest
5 голосов
/ 19 сентября 2019
template <typename T>
void call(T) {  //#1
    std::cout << "1" << std::endl;
}

template <typename T, typename...Args>
void call(T, Args...) {  //#2
    std::cout << "2" << std::endl;
}

Когда я вызываю такую ​​функцию,

call(10);

GCC, Clang и MSVC все используют # 1.

Однако правило частичного упорядочения в стандарте гласит:

Если объявление параметра, соответствующее P i , является пакетом параметров функции, то тип его идентификатор-объявления сравнивается с каждым оставшимся параметромвведите параметр-тип-списка из A. Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции.Во время частичного упорядочения, если A i изначально был пакетом параметров функции:

  • (10.1), если P не содержит тип параметра функции, соответствующий A i , тогда A i игнорируется;

  • (10.2) в противном случае, если P i не является пакетом параметров функции, сбой вывода аргумента шаблона.

Когда мы выводим # 1 из # 2, с T, Args... как A, Tкак P, P не содержит аргумента шаблона, соответствующего Args....Args... игнорируется, поэтому # 1 может быть успешно выведен из # 2.

Тогда вывод # 2 из # 1 с T в качестве A и T, Args... в качестве P также успешно приводит к T = T, Args... = {}.

Поэтому, согласно правилам частичного упорядочения, при вызове call(10) компилятор должен выдавать неоднозначную ошибку, но на самом деле весь компилятор называется # 1, почему это так?

1 Ответ

3 голосов
/ 19 сентября 2019

Компиляторы верны.#1 более специализирован, чем #2.


Правило для пакетов параметров шаблона частичного упорядочения указано в [temp.deduct.partial] / 8 :

Используя результирующие типы P и A, затем производится вычет, как описано в [temp.deduct.type].Если P является пакетом параметров функции, тип A каждого оставшегося типа параметра шаблона аргумента сравнивается с типом P идентификатора объявления 1017 * пакета параметров функции.Каждое сравнение выводит аргументы шаблона для последующих позиций в пакетах параметров шаблона, расширенных пакетом параметров функции.Аналогично, если A был преобразован из пакета параметров функции, он сравнивается с каждым оставшимся типом параметра в шаблоне параметров.Если для данного типа выведение выполняется успешно, считается, что тип из шаблона аргумента, по крайней мере, такой же специализированный, как и тип из шаблона параметра.

Когда шаблон параметра равен #1 и аргументшаблон #2, идентификатор объявления Args (который является типом) из #2 сравнивается с каждым из оставшихся параметров в #1 (его нет).Следовательно, #2 является, по крайней мере, таким же специализированным, как #1.

Когда шаблон параметра равен #2, а шаблон аргумента #1, идентификатор объявления Args (который является типом) из #2 сравнивается с каждым из оставшихся параметров в #1 (его нет).Следовательно, #1 по крайней мере так же специализирован, как и #2.

Кажется двусмысленным, верно?Теперь у нас есть прерыватель связи, [temp.deduct.partial] / 11 :

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

Очевидно, #1 более специализирован, чем #2.

...