Поведение функции шаблона С++
Скажем, у меня есть эта функция:
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 && станут ошибками, так как вы не можете ссылаться на ссылку.