Допустим, у меня есть эта функция:
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);
Почему это так? Почему компилятор не жалуется в этом случае?
Поскольку существует аргумент шаблона, происходит свертывание ссылок. Это то, что Скотт Мейерс называет универсальные ссылки. U&&
на самом деле станет int &
, Есть хороший статья и видео о том, как это работает и как это можно использовать.
Согласно § 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&
Это происходит из-за правил свертывания ссылок, добавленных в c ++ 11
A& & becomes A&
A& && becomes A&
A&& & becomes A&
A&& && becomes A&&
В шаблонах эти правила применяются, но не в обычной функции, при этом ссылка не сворачивается в функции. Существуют и другие конкретные ситуации, в которых происходит свертывание ссылок, например, при наличии auto
, decltype
или typedef
(это включает using
декларации) Это объясняет результаты вашей компиляции. Свертывание ссылок должно было быть добавлено в c ++ 11, потому что в противном случае использовались бы ссылки типа A & & станет ошибкой, так как вы не можете иметь ссылку на ссылку.