Вычитание относится к процессу определения типа параметра шаблона из заданного аргумента. Это применимо к шаблонам функций, 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>
struct type_identity {
using type = T;
};
template <typename T>
bool binary_function(Foo<T> lhs, typename type_identity<Foo<T>>::type rhs)
{
return binary_function(lhs, rhs);
}
(Возможно, у вас возникли такие проблемы с дедукцией с чем-то вроде std::min(1U, 2L)
.)
person
Kerrek SB
schedule
11.08.2014