Ответ 1
Это не значит, что std::make_pair
предназначен для использования; вы не должны явно указывать аргументы шаблона.
С++ 11 std::make_pair
принимает два аргумента типа T&&
и U&&
, где T
и U
являются параметрами типа шаблона. Фактически, он выглядит так (игнорируя тип возврата):
template <typename T, typename U>
[return type] make_pair(T&& argT, U&& argU);
Когда вы вызываете std::make_pair
и явно указываете аргументы типа шаблона, дедукция аргументов не выполняется. Вместо этого аргументы типа заменяются непосредственно в объявлении шаблона, давая:
[return type] make_pair(std::string&& argT, int&& argU);
Обратите внимание, что оба этих типа параметров являются ссылками rvalue. Таким образом, они могут связываться только с rvalues. Это не проблема для второго аргумента, который вы передаете, 7
, потому что это выражение rvalue. s
, однако, является выражением lvalue (оно не является временным и не перемещается). Это означает, что шаблон функции не соответствует вашим аргументам, поэтому вы получаете ошибку.
Итак, почему это работает, когда вы явно не указываете, что T
и U
находятся в списке аргументов шаблона? Короче говоря, ссылочные параметры rvalue являются особыми в шаблонах. Отчасти благодаря языковой функции, называемой ссылочным коллапсированием, ссылочный параметр rvalue типа A&&
, где A
является параметром типа шаблона, может связываться с любым типом A
.
Не имеет значения, является ли A
lvalue, rvalue, const-квалифицированным, нестабильным или неквалифицированным, A&&
может привязываться к этому объекту (опять же, если и только если A
сам является параметром шаблона).
В вашем примере мы вызываем вызов:
make_pair(s, 7)
Здесь s
является lvalue типа std::string
и 7
является значением r типа int
. Поскольку вы не указываете аргументы шаблона для шаблона функции, выполняется вывод аргумента шаблона, чтобы выяснить, что такое аргументы.
Чтобы связать s
, lvalue, с T&&
, компилятор выводит T
как std::string&
, давая аргумент типа std::string& &&
. Однако ссылок на ссылки нет, поэтому эта "двойная ссылка" рушится, чтобы стать std::string&
. s
является совпадением.
Простое связывание 7
с U&&
: компилятор может вывести U
как int
, указав параметр типа int&&
, который успешно связывается с 7
, потому что это значение r.
Есть много тонкостей с этими новыми языковыми функциями, но если вы следуете одному простому правилу, это довольно легко:
Если аргумент шаблона можно вывести из аргументов функции, пусть это будет выведено. Не указывайте явно аргумент, если вы абсолютно не обязаны.
Пусть компилятор выполнит тяжелую работу, и в 99,9% случаев это будет именно то, что вы хотели в любом случае. Когда это не то, что вы хотели, вы обычно получаете ошибку компиляции, которую легко идентифицировать и исправить.