А.1.2. Ссылки на r-значения и шаблоны функций
Еще один нюанс имеет отношение к использованию ссылок на r-значения в качестве параметров шаблона функции: если параметр функции — ссылка на r-значение типа параметра шаблона, механизм автоматического выведения типа аргумента шаблона заключает, что тип — это ссылка на l-значение, если функции передано l-значение, или обычный не-ссылочный тип, если передано r-значение. Фраза получилась довольно запутанной, поэтому приведём пример. Рассмотрим такую функцию:
template<typename T>
void foo(T&& t) {}
Если при вызове передать ей r-значение, как показано ниже, то в качестве T выводится тип этого значения:
foo(42);
foo(3.14159);
fоо(std::string());
Но если вызвать foo, передав l-значение, то механизм выведения типа решит, что T — ссылка на l-значение:
int i = 42;
foo(i);
Поскольку объявлено, что параметр функции имеет тип T&&, то получается, что это ссылка на ссылку, и такая конструкция трактуется как обычная одинарная ссылка. Таким образом, сигнатура функции foo<int&>() такова:
void foo<int&>(int& t);
Это позволяет одному шаблону функции принимать параметры, являющиеся как l-, так и r-значениями. В частности, это используется в конструкторе std::thread (см. разделы 2.1 и 2.2), чтобы в случае, когда переданный допускающий вызов объект является r-значением, его можно было бы не копировать, а переместить во внутреннюю память.