Итак, вы спрашиваете, в основном, почему следующая программа печатает Special foo
, но Generic bar
:
struct A {};
template<class ... Args>
void foo(Args&&...)
{
std::cout << "Generic foo\n";
}
void foo(A)
{
std::cout << "Special foo\n";
}
template<class ... Args>
void bar(Args&&...)
{
std::cout << "Generic bar\n";
}
void bar(A const&)
{
std::cout << "Special bar\n";
}
int main()
{
A a;
foo(a);
bar(a);
}
Давайте посмотрим, что происходит для разрешения перегрузки:
1 , Выбираются функции-кандидаты.
C++11/[over.match.funcs]/7
В каждом случае, когда кандидат является шаблоном функции, специализации шаблона-кандидата генерируются с использованием вывода аргумента шаблона (14.8.3, 14.8.2). Затем эти кандидаты обрабатываются как функции-кандидаты обычным способом.
Кандидаты на вызов foo(a)
:
template<> void foo<A&>(A&); // reference collapsing
void foo(A);
Кандидаты на вызов bar(a)
:
template<> void bar<A&>(A&);
void bar(A const&);
2. Выбор наилучшей жизнеспособной функции:
Во-первых, перегрузка лучше, если (по крайней мере) один из параметров имеет лучшую последовательность преобразования (и ни один другой не имеет худшую последовательность преобразования).
C++11/[over.ics.rank]/3
Стандартная последовательность преобразования S1 является лучшей последовательностью преобразования, чем стандартная последовательность преобразования S2, если [...] S1 и S2 являются ссылочными привязками (8.5.3), и типы, на которые ссылаются ссылки, являются одним и тем же типом, за исключением cv-квалификаторов верхнего уровня, и тип, на который ссылается ссылка, инициализированная S2, является более квалифицированным для cv , чем тип, на который ссылается ссылка инициализируется ссылкой S1.
Это приводит к предпочтению шаблона-кандидата для bar
, поскольку преобразование, требуемое для вызова void bar(A const&)
, требует связывания lvalue с более квалифицированной cv ссылкой const lvalue. Таким образом, вы видите версию c generi, вызываемую при использовании Distances const&
.
C++11/[over.best.ics]/6
Когда тип параметра не является ссылкой [...]
Когда параметр имеет тип класса, а выражение аргумента имеет тот же тип, последовательность неявного преобразования является преобразованием идентичности.
Это делает последовательность преобразования для параметра a
при передаче в void foo(A)
преобразование идентификатора (что также имеет место для функции шаблона).
Если ни одна из перегрузок не имеет лучшей последовательности преобразования, то версия без шаблона выигрывает у шаблона.
C++11/[over.match.best]/1
[...] С учетом этих определений, жизнеспособная функция F1 определяется как лучшая функция, чем другая жизнеспособная функция F2, если [...] F1 является не шаблонной функцией, а F2 является функцией специализация шаблона.
Это относится к foo
и заставляет ваш код работать так, как вы предполагали, когда вы используете Distances distances
.