Что такое невыведенный контекст?

Я наткнулся на "Почему аргумент шаблона дедукция здесь не работает? ", и ответы можно свести к" Это невыведенный контекст ".

В частности, первый говорит, что это такая вещь, а затем перенаправляет на стандарт для «деталей», а второй цитирует стандарт, что, мягко говоря, загадочно.

Может ли кто-нибудь объяснить простым смертным, таким как я, что такое невыявленный контекст, когда это происходит и почему?


person Shoe    schedule 11.08.2014    source источник
comment
Связано: C ++, аргумент шаблона не может быть выведен   -  person Shafik Yaghmour    schedule 11.08.2014


Ответы (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>
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
comment
Итак, вы ссылаетесь на второй случай, описанный стандартом (тип, который является идентификатором шаблона, в котором один или несколько аргументов шаблона являются выражением, которое ссылается на параметр шаблона.), Верно? Если да, можете ли вы сделать пример первого (спецификатор вложенного имени типа, который был указан с использованием квалифицированного идентификатора)? - person Shoe; 11.08.2014
comment
Собственно, теперь, когда я внимательно его прочитал, я думаю, что все наоборот. - person Shoe; 11.08.2014
comment
@ Джефффри: Может, что-то вроде template <std::size_t> struct Bar; template <typename T> void(Bar<sizeof(T)>);? - person Kerrek SB; 11.08.2014
comment
Дело в следующем: существует взаимно однозначное соответствие между типами T и классами шаблонов Foo<T>, поэтому вы можете вывести первое из второго. Но нет никакого соответствия между типами T и произвольными членами Foo<T>::X. - person Kerrek SB; 11.08.2014
comment
Мне кажется, вы также можете ответить на это. - person Baum mit Augen; 11.08.2014
comment
Отличный ответ и содержательный пример. - person alecov; 16.08.2016
comment
@KerrekSB Цель состоит в том, чтобы сэкономить время компиляции, верно? Как вы сказали, для g (0.1) он может возвращать неоднозначное значение, g (1) возвращает ошибку, g (true) do может выводить float - person camino; 26.10.2018
comment
@camino: нет, я бы так не сказал - это не что-то техническое или механическое. В принципе существует гораздо более фундаментальная проблема, заключающаяся в том, что отображение имени в тип необратимо, и поэтому нет смысла брать тип и запрашивать имя, которое его определяет, потому что этого самого понятия не существует. Дедукция работает только для гораздо более узкого вопроса о том, какой тип можно подставить в этот конкретный данный шаблон, чтобы сформировать действительный вызов, который гораздо более ограничен и, следовательно, имеет смысл. - person Kerrek SB; 26.10.2018
comment
@KerrekSB Спасибо! - person camino; 26.10.2018
comment
возможно, обновите ответ, указав ссылку на std::type_identity. Поиски привели меня сюда, и ответ помог мне найти его, но мне потребовалось два клика вместо одного;) - person 463035818_is_not_a_number; 21.05.2021