Ответ 1
Удержание относится к процессу определения типа параметра шаблона из заданного аргумента. Это относится к шаблонам функций, auto
и в некоторых других случаях (например, частичная специализация). Например, рассмотрим:
template <typename T> void f(std::vector<T>);
Теперь, если вы скажете f(x)
, где вы объявили std::vector<int> x;
, тогда T
выводится как int
, и вы получаете специализацию f<int>
.
Чтобы дедукция работала, тип параметра шаблона, который должен быть выведен, должен отображаться в выводимом контексте. В этом примере параметр функции f
является таким выводимым контекстом. То есть аргумент в выражении вызова функции позволяет нам определить, каким должен быть параметр шаблона T
, чтобы выражение вызова было действительным.
Тем не менее, существуют также не выведенные контексты, где вычет невозможен. Каноническим примером является "параметр шаблона, который появляется слева от ::
template <typename> struct Foo;
template <typename T> void g(typename Foo<T>::type);
В этом шаблоне функции T
в списке параметров функции находится в невыбранном контексте. Таким образом, вы не можете сказать g(x)
и вывести T
Причина этого заключается в том, что не существует "обратного соответствия" между произвольными типами и членами Foo<T>::type
. Например, вы можете иметь специализации:
template <> struct Foo<int> { using type = double; };
template <> struct Foo<char> { using type = double; };
template <> struct Foo<float> { using type = bool; };
template <> struct Foo<long> { int type = 10; };
template <> struct Foo<unsigned> { };
Если вы вызываете g(double{})
есть два возможных ответа для T
, а если вы вызываете g(int{})
ответа нет. В общем, нет никакой связи между параметрами шаблона класса и членами класса, поэтому вы не можете выполнить какой-либо разумный вывод аргумента.
Иногда полезно явно запретить вывод аргументов. Это, например, случай для std::forward
. Другой пример - когда у вас есть преобразования из Foo<U>
в Foo<T>
, скажем, или другие преобразования (например, std::string
и char const *
). Теперь предположим, что у вас есть бесплатная функция:
template <typename T> bool binary_function(Foo<T> lhs, Foo<T> rhs);
Если вы вызываете binary_function(t, u)
, то вычет может быть неоднозначным и, следовательно, потерпеть неудачу. Но разумно вывести только один аргумент, а не вывести другой, что позволяет неявные преобразования. Теперь необходим явно не выводимый контекст, например, такой:
template <typename T>
bool binary_function(Foo<T> lhs, typename std::common_type<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(Вы могли испытывать такие проблемы с дедукцией с чем-то вроде std::min(1U, 2L)
.)