Обязательный приоритет переадресации
Может кто-нибудь, пожалуйста, помогите мне понять, почему следующий код выводит T&&
и не const A&
:
class A{};
template< typename T >
void foo( T&& )
{
std::cout << "T&&" << std::endl;
}
void foo( const A& )
{
std::cout << "const A&" << std::endl;
}
int main()
{
A a;
foo( a );
}
1 ответ
[temp.deduct.call] / 1 & 3 [выделено мое]:
/ 1
Вывод аргумента шаблона выполняется путем сравнения каждого типа параметра шаблона функции (вызовите его
P
), который содержит параметры шаблона, которые участвуют в выводе аргумента шаблона с типом соответствующего аргумента вызова (вызовите егоA
), как описано ниже..../ 3
... Ссылка на пересылку - это rvalue-ссылка на cv-неквалифицированный параметр шаблона, который не представляет параметр шаблона шаблона класса (во время вывода аргумента шаблона класса ([over.match.class.deduct])). Если
P
является ссылкой для пересылки, а аргумент является lvalue, тип "lvalue ссылка наA
”Используется вместоA
для вывода типа.[ Пример:
... template <class T> int f(T&& heisenreference); int i; int n1 = f(i); // calls f<int&>(int&) ...
- конец примера]
Применительно к вашему примеру, foo(a)
вызов будет, для вывода аргумента шаблона, решить void foo<A&>(A&)
, что является точным соответствием для cv-безусловного lvalue a
, Не шаблонная функция void foo(const A&)
, by [over.ics.rank] /3.2.6 (спасибо @MM за исправление правила ранжирования, которое устраняет неоднозначность этих двух), обеспечит худшую последовательность преобразования, чем вычтенная из шаблона, и void foo<A&>(A&)
будет иметь приоритет в разрешении перегрузки.
/3.2
Стандартная последовательность преобразования
S1
является лучшей последовательностью преобразования, чем стандартная последовательность преобразованияS2
если
...
/3.2.6 -
S1
а такжеS2
являются привязками ссылок (8.5.3), и типы, на которые ссылаются ссылки, относятся к одному и тому же типу, за исключением cv-квалификаторов верхнего уровня, и типа, к которому ссылка инициализируетсяS2
ссылка более квалифицирована по cv, чем тип, к которому ссылка инициализированаS1
относится.
Если вы измените свой пример так, чтобы не шаблонная перегрузка имела те же cv-квалификаторы верхнего уровня, что и вычтенный шаблон (например, путем удаления const
cv-квалификатор из не шаблонной функции), это приведет к тому, что априор будет выбран разрешением перегрузки,.
class A {};
template< typename T >
void foo( T&& )
{
std::cout << "T&&" << std::endl;
}
void foo( A& )
{
std::cout << "A&" << std::endl;
}
int main()
{
A a;
foo( a ); // "A&"
}