Почему const lvalue привязываются иначе, чем const rvalue с учетом перегрузок T&& и const T&?

Этот код (доступен по адресу http://ideone.com/Mo7fQr)

template<typename T>
void f(const T&) { std::cout << "const T& overload\n"; }

template<typename T>
void f(T&&) { std::cout << "T&& overload\n"; }

int main()
{
  const int x = 0;

  f(x);                 // calls const T& overload
  f(std::move(x));      // calls T&& overload
}

первый вызов f (с lvalue) вызывает перегрузку const T&, а второй вызов (с rvalue) вызывает перегрузку T&&. По крайней мере, это происходит с gcc 4.8.1 и самой последней версией VC++ (VC12).

Думаю, я понимаю, почему второй вызов разрешается именно так: потому что первый шаблон создает экземпляр с параметром const int&, в то время как второй шаблон создает экземпляр с параметром const int&&, и поскольку аргумент, передаваемый на сайте вызова, является значением r, он предпочтительнее привязывается к ссылке rvalue. (Я полагаю, что это указано в стандарте С++ 11 в 13.3.3.2/3, пункт 1, пункт 4.)

Но при первом вызове f оба шаблона создают экземпляры, принимающие параметр типа const int&. Так почему же первый шаблон предпочтительнее, когда передается const lvalue?


person KnowItAllWannabe    schedule 20.07.2013    source источник


Ответы (1)


Когда одна и та же специализация шаблона функции может быть сгенерирована более чем из одного объявления, неоднозначность объявлений устраняется с помощью частичного упорядочения шаблонов функций, как описано в стандарте C++11 §14.5.6.2 Частичное упорядочение шаблонов функций [temp.func.order]. Компилятор определяет, какой из шаблонов является наиболее специализированным, и предпочитает его.

В вашем примере перегрузка const T& для f более специализирована, чем перегрузка T&&. Интуитивно T&& можно вывести из всего, что может const T&, но не наоборот, поэтому const T& является более конкретным, и, следовательно, его перегрузка функций является более специализированной.

person Casey    schedule 20.07.2013
comment
Как T const& может не совпадать с тем, что может T&&? Есть пример? (Я думаю, что на самом деле может соответствовать чему угодно, но для некоторых вещей, таких как неконстантные lvalue и rvalue, потребуется корректировка квалификации. Кроме того, для rvalue T&& предпочтительнее T const&, вот и все.) - person Xeo; 20.07.2013
comment
@Xeo, например, T const& не может вывести int& или int&&. Если вы попытались вызвать f с параметром int&, перегрузка f(const T&) специализирована для f(const int&) — в частности, не f(int&) — и требует последовательности преобразования int& -> const int&. - person Casey; 20.07.2013
comment
Я заменил match на be deduced to в ответе, думаю, так немного понятнее. - person Casey; 20.07.2013