Подтвердить что ты не робот

Что такое неопределенный контекст?

Недавно я столкнулся с этим вопросом, и ответы можно суммировать с "Это несоответствующий контекст".

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

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

4b9b3361

Ответ 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).)