А.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-значением, его можно было бы не копировать, а переместить во внутреннюю память.