Вывод типа для нежизнеспособных шаблонов функций

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

template<class T>
void foo(T, int);

foo(42); // the template specialization foo<int>(int, int) is not viable

В контексте другого вопроса важно, является ли успешным вывод типа для шаблона функции (и происходит ли замена):

template<class T>
struct has_no_nested_type {};

// I think you need some specialization for which the following class template
// `non_immediate_context` can be instantiated, otherwise the program is
// ill-formed, NDR
template<>
struct has_no_nested_type<double>
{ using type = double; };

// make the error appear NOT in the immediate context
template<class T>
struct non_immediate_context
{
    using type = typename has_no_nested_type<T>::type;
};


template<class T>
typename non_immediate_context<T>::type
foo(T, int) { return {}; }

template<class T>
bool foo(T) { return {}; }


int main()
{
    foo(42);      // well-formed? clang++3.5 and g++4.8.2 accept it
    foo<int>(42); // well-formed? clang++3.5 accepts it, but not g++4.8.2
}

При создании первого шаблона функции foo за T == int, замена производит недопустимый тип не в непосредственном контексте foo, Это приводит к серьезной ошибке (вот о чем связан этот вопрос.)

Однако при сдаче foo вывести его аргумент шаблона, g++ и clang++ соглашаются, что создание экземпляра не происходит. Как объясняет Йоханнес Шауб, это происходит из-за "ошибки совпадения".

Вопрос: Что такое "ошибка соответствия", и где и как она указана в Стандарте?

Альтернативный вопрос: почему есть разница между foo(42) а также foo<int>(42) для g++?


Что я нашел / попробовал до сих пор:

[over.match.funcs]/7 и [temp.over] описывают особенности разрешения перегрузки для шаблонов функций. Последние, кажется, требуют замены шаблонных параметров для foo,

Интересно, что [over.match.funcs]/7 запускает процесс, описанный в [temp.over], перед проверкой на жизнеспособность шаблона функции (специализация). Точно так же вычитание типа не учитывает, скажем, аргументы функции по умолчанию (кроме того, что делает их не выводимым контекстом). Насколько я могу судить, это не касается жизнеспособности.

Другим, возможно, важным аспектом является способ определения типа вывода. Он действует на параметры одной функции, но я не вижу, где проводится различие между типами параметров, которые содержат / зависят от параметров шаблона (например, T const&) и те, которые не (как int).

Тем не менее, g++ делает различие между явным указанием параметра шаблона (серьезная ошибка) и разрешением их вывода (ошибка вывода / SFINAE). Зачем?

1 ответ

Я подытожил процесс, описанный в 14.8.2.1p1

Вывод аргумента шаблона выполняется путем сравнения каждого типа параметра шаблона функции (назовите его P) с типом соответствующего аргумента вызова (назовите его A), как описано ниже.

В нашем случае мы имеем для P (T, int) и для А мы имеем (int), Для первой пары P/A, которая T против intмы можем соответствовать T в int (согласно процессу, описанному в 14.8.2.5). Но для второй "пары" мы имеем int но не имеют аналогов. Таким образом, вычет не может быть сделан для этой "пары".

Таким образом, в соответствии с 14.8.2.5p2: "Если выведение типа не может быть выполнено для любой пары P/A,..., выведение аргумента шаблона завершается неудачно".

Тогда вы никогда не дойдете до того момента, когда подставите аргументы шаблона в шаблон функции.

Все это, вероятно, может быть более точно описано в Стандарте (IMO), но я считаю, что именно так можно реализовать вещи, соответствующие реальному поведению Clang и GCC, и это кажется разумной интерпретацией Standardese.

Другие вопросы по тегам