Поведение функции шаблона С++

Скажем, у меня есть эта функция:

bool f(int&& one, int&& two) { }

Если я попытаюсь вызвать его с помощью этого кода:

int x = 4;
f(x, 5);

компилятор будет жаловаться, что он не может преобразовать x из ссылки lvalue в ссылку rvalue, что является правильным.

Теперь, если я преобразую f в функцию шаблона, например:

template <class T, class U>
bool f(T&& one, U&& two) { }

то я могу вызвать его с помощью ссылки lvalue:

int x = 5;
f(x, 5);

Почему так? Почему компилятор не жалуется в этом случае?

Ответы

Ответ 1

Поскольку существует вывод аргумента шаблона, происходит свертывание ссылок. Это то, что Скотт Мейерс называет универсальными ссылками. U&& фактически станет int &. Существует хорошая статья статьи и видео о том, как она работает и как ее можно использовать.

Ответ 2

В § 8.3.3/6. Это правило сбрасывания ссылок.

template <class T> void func(T&&)  // Accepts rvalue or lvalue
void func(T&&)                     // Accepts rvalue only
void func(T&)                      // Accepts lvalue only

Стоит пример из стандартной черновики:

int i;
typedef int& LRI;
typedef int&& RRI;

LRI& r1 = i;           // r1 has the type int&
const LRI& r2 = i;     // r2 has the type int&
const LRI&& r3 = i;    // r3 has the type int&

RRI& r4 = i;           // r4 has the type int&
RRI&& r5 = 5;          // r5 has the type int&&

decltype(r2)& r6 = i;  // r6 has the type int&
decltype(r2)&& r7 = i; // r7 has the type int&

Ответ 3

Это происходит из-за правил сбрасывания ссылок, добавленных в С++ 11

A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&

В шаблонах эти правила применяются, но не в нормальной функции нет нормального обращения в функции. Существуют и другие специфические ситуации, когда происходит свертывание ссылок, например, в присутствии auto, decltype или typedef (включая объявления using). Это объясняет результаты вашей компиляции. В С++ 11 должно было быть добавлено обращение к коллапсу, поскольку в противном случае использовались бы такие ссылки, как A && станут ошибками, так как вы не можете ссылаться на ссылку.