Обязательный приоритет переадресации

Может кто-нибудь, пожалуйста, помогите мне понять, почему следующий код выводит 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&"
}
Другие вопросы по тегам