c++ templates c++11 language-lawyer sfinae

¿Qué es exactamente el "contexto inmediato" mencionado en el Estándar C++ 11 para el cual se aplica SFINAE?



templates c++11 (2)

El párrafo 14.8.2 / 8 de la Norma C ++ 11 especifica las condiciones bajo las cuales una falla de sustitución debe o no dar como resultado un error de compilación "dura" (causando por lo tanto la falla de la compilación) o un error "suave" que simplemente hacer que el compilador descarte una plantilla de un conjunto de candidatos para la resolución de sobrecarga (sin hacer que falle la compilación y habilite la conocida expresión SFINAE):

Si una sustitución da como resultado un tipo o expresión no válida, el tipo de deducción falla. Un tipo o expresión inválida es aquella que estaría mal formada si se escribiera utilizando los argumentos sustituidos. [Nota: la verificación de acceso se realiza como parte del proceso de sustitución. -Finalización] Solo los tipos y expresiones inválidos en el contexto inmediato del tipo de función y sus tipos de parámetros de plantilla pueden dar como resultado una falla de deducción . [...]

Las palabras " contexto inmediato " aparecen solo 8 veces en todo el estándar C ++ 11, y cada vez son seguidas por (u ocurren como parte de) una instancia del siguiente texto (no normativo):

[Nota: la evaluación de los tipos y expresiones sustituidos puede dar como resultado efectos secundarios tales como la instanciación de especializaciones de plantilla de clase y / o especializaciones de plantilla de función, la generación de funciones implícitamente definidas, etc. Tales efectos secundarios no están en el "inmediato contexto "y puede dar lugar a que el programa esté mal formado. -Finalizar nota]

La nota da una sugerencia (no muy generosa) sobre lo que se entiende por contexto inmediato , pero al menos para mí, esto a menudo no es suficiente para decidir si se supone o no que una sustitución causa un error de compilación "difícil".

PREGUNTA:

¿Podría proporcionar una explicación, un procedimiento de decisión y / o algunos ejemplos concretos para ayudar a determinar en qué casos ocurre y no ocurre un error de sustitución en el " contexto inmediato " del tipo de función y sus tipos de parámetros de plantilla?


El contexto inmediato es básicamente lo que ves en la declaración de la plantilla en sí. Todo lo que está fuera de eso es un error difícil. Ejemplos de Hard-error:

#include <type_traits> template<class T> struct trait{ using type = typename T::type; }; template<class T, class U = typename trait<T>::type> void f(int); void f(...); template<class T, class U = typename T::type> void g(int); void g(...); template<class> struct dependent_false : std::false_type{}; template<class T> struct X{ static_assert(dependent_false<T>(), "..."); using type = void; }; int main(){ f<int>(0); g<X<int>>(0); }

Versión en vivo.


Si considera todas las plantillas y funciones implícitamente definidas que son necesarias para determinar el resultado de la sustitución del argumento de la plantilla, e imagina que se generan primero, antes de que comience la sustitución, entonces los errores que se producen en ese primer paso no están en el contexto inmediato. y resulta en errores difíciles.

Si todas esas instancias y definiciones implícitamente (que pueden incluir funciones de definición como eliminadas) pueden realizarse sin error, entonces cualquier otro "error" que ocurra durante la sustitución (es decir, al referirse a las plantillas creadas y las funciones implícitamente definidas en la plantilla de la función firma) no son errores, sino que resultan en fallas de deducción.

Entonces, se le da una plantilla de función como esta:

template<typename T> void func(typename T::type* arg);

y un "repliegue" que se usará si la deducción falla para la otra función:

template<typename> void func(...);

y una plantilla de clase como esta:

template<typename T> struct A { typedef T* type; };

Una llamada a func<A<int&>>(nullptr) sustituirá a A<int&> por T y para comprobar si existe T::type , debe instanciar A<int&> . Si imaginamos poner una instanciación explícita antes de la llamada a func<A<int&>(nullptr) :

template class A<int&>;

entonces eso fallaría, porque intenta crear el tipo int&* y los punteros a las referencias no están permitidos. No llegamos al punto de verificar si la sustitución tiene éxito, porque hay un error grave al crear instancias de A<int&> .

Ahora digamos que hay una especialización explícita de A :

template<> struct A<char> { };

Una llamada a func<A<char>>(nullptr) requiere la instanciación de A<char> , así que imagine una instanciación explícita en algún lugar del programa antes de la llamada:

template class A<char>;

Esta instanciación está bien, no hay ningún error a partir de esto, por lo que procedemos a la sustitución de argumentos. La creación de instancias de A<char> funcionó, pero A<char>::type no existe, pero está bien porque solo se hace referencia en la declaración de func , por lo que solo hace que la deducción de argumentos falle, y el error de recuperación ... .función se llama en su lugar.

En otras situaciones, la sustitución puede hacer que las funciones especiales de los miembros se definan implícitamente, posiblemente como eliminadas, lo que podría desencadenar otras instancias o definiciones implícitas. Si ocurren errores durante esa etapa de "generación de instancias y definiciones implícitas", entonces son errores, pero si eso sucede pero durante la sustitución una expresión en la firma de la plantilla de función resulta ser inválida, por ejemplo, porque usa un miembro que no existe o algo que se definió implícitamente como eliminado, eso no es un error, solo un error de deducción.

Entonces el modelo mental que uso es que la sustitución necesita hacer un paso de "preparación" primero para generar tipos y miembros, lo que puede causar errores difíciles, pero una vez que tenemos toda la generación necesaria, cualquier otro uso inválido no es un error. Por supuesto, todo lo que hace es mover el problema de "¿qué significa el contexto inmediato ?" a "¿Qué tipos y miembros deben generarse antes de que se pueda verificar esta sustitución?" ¡así que puede que te ayude o no!