Ответ 1
Если вы рассматриваете все шаблоны и неявно определенные функции, необходимые для определения результата замены аргумента шаблона, и представьте, что они сгенерированы сперва, перед началом замещения, то любые ошибки, возникающие на этом первом этапе, не находятся в немедленный контекст и приводит к жестким ошибкам.
Если все эти экземпляры и неявно-определения (которые могут включать определение функций как удаленных) могут быть выполнены без ошибок, то любые дальнейшие "ошибки", возникающие во время подстановки (т.е. при обращении к экземпляру шаблонов и неявно определенным функциям в подпись шаблона функции) не являются ошибками, но приводят к ошибкам вычета.
Итак, задайте шаблон функции следующим образом:
template<typename T>
void
func(typename T::type* arg);
и "fall-back", которые будут использоваться, если дедукция не будет выполнена для другой функции:
template<typename>
void
func(...);
и шаблон класса следующим образом:
template<typename T>
struct A
{
typedef T* type;
};
Вызов func<A<int&>>(nullptr)
заменит A<int&>
на T
, и чтобы проверить, существует ли T::type
, он должен создать экземпляр A<int&>
. Если мы предположим, что перед вызовом func<A<int&>(nullptr)
было создано явное инстанцирование:
template class A<int&>;
то это не сработает, потому что он пытается создать тип int&*
, а указатели на ссылки не разрешены. Мы не добираемся до проверки того, что замена выполняется успешно, потому что существует сложная ошибка при создании экземпляра A<int&>
.
Теперь скажем там явную специализацию A
:
template<>
struct A<char>
{
};
Вызов func<A<char>>(nullptr)
требует создания экземпляра A<char>
, поэтому представьте себе явное инстанцирование где-то в программе перед вызовом:
template class A<char>;
Этот экземпляр в порядке, нет ошибки, поэтому переходим к замене аргументов. Создание экземпляра A<char>
сработало, но A<char>::type
не существует, но это нормально, потому что он ссылается только на объявление func
, поэтому только вывод аргумента не выполняется, а функция fall-back ...
получает вместо этого.
В других ситуациях подстановка может приводить к тому, что специальные функции-члены должны быть неявно определены, возможно, как удаленные, что может вызвать другие экземпляры или неявные определения. Если во время этого этапа генерации экземпляров и неявных определений возникают ошибки, то это ошибки, но если это удается, но во время подстановки выражение в сигнатуре шаблона функции оказывается недопустимым, например. потому что он использует член, который не существует, или что-то, что неявно определено как удаленное, что не является ошибкой, просто сбой дедукции.
Таким образом, ментальная модель, которую я использую, заключается в том, что подстановка должна сначала выполнить "подготовительный" шаг для генерации типов и членов, что может привести к жестким ошибкам, но как только у нас будет все необходимое поколение, любые дополнительные недопустимые применения не являются ошибками, Конечно, все это значит переместить проблему из "что означает немедленный контекст"? к "Какие типы и члены должны быть созданы до того, как эту замену можно проверить?" так что это может или не поможет вам!