c++ - funciones - SFINAE y especializaciones de plantillas de clases parciales.
plantillas en c++ (1)
He estado usando enfoques basados en SFINAE durante bastante tiempo, especialmente para habilitar / deshabilitar especializaciones de plantillas de clase específicas a través de std::enable_if
.
Por lo tanto, me sorprendió un poco leer el documento que describe el void_t
alias propuesto / idioma de detección:
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4502.pdf
La sección 4 está dedicada a la discusión de la validez de la expresión idiomática y se refiere a una discusión en la que dos partes discuten sobre la aplicabilidad de SFINAE en las especializaciones de plantillas de clase parciales (con Richard Smith que señala que la norma carece de texto sobre este tema) . Hacia el final de la sección, se menciona el siguiente problema de CWG
http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#2054
Aquí nuevamente se afirma que la norma no permite explícitamente el ejemplo reproducido en el problema.
Estoy un poco confundido porque me parece que, por ejemplo, el uso de enable_if
en especializaciones parciales ha sido una práctica estándar durante bastante tiempo (consulte, por ejemplo, la documentación de Boost, que menciona explícitamente las especializaciones parciales).
¿Estoy malinterpretando los puntos en los documentos anteriores o es realmente una zona gris?
Me gustaría argumentar que el Estándar no es compatible con SFINAE en especializaciones parciales, debido a un defecto de redacción. Empecemos con [temp.class.spec.match]:
Una especialización parcial coincide con una lista de argumentos de plantilla real dada si los argumentos de plantilla de la especialización parcial se pueden deducir de la lista de argumentos de plantilla real (14.8.2).
Y, desde [temp.deduct], la cláusula SFINAE:
Si una sustitución produce un tipo o expresión no válida, la deducción de tipo falla. Un tipo o expresión inválida es aquella que no se formará correctamente, con un diagnóstico requerido, si se escribe usando los argumentos sustituidos. [Nota: Si no se requiere ningún diagnóstico, el programa aún está mal formado. El control de acceso se realiza como parte del proceso de sustitución. —Endente final] Solo los tipos y expresiones no válidos en el contexto inmediato del tipo de función y los tipos de parámetros de su plantilla pueden dar como resultado un error de deducción.
El ejemplo ligeramente modificado † de CWG es:
template <class T, class U> struct X {
typedef char member;
};
template<class T> struct X<T,
typename enable_if<(sizeof(T)>sizeof(
float)), float>::type>
{
typedef long long member;
};
int main() {
cout << sizeof(X<char, float>::member);
}
La búsqueda de nombres en X
encuentra el primario, con T == char, U == float
. Nos fijamos en la especialización parcial y vemos si "coincide", lo que significa que los argumentos de la plantilla "pueden deducirse", es decir:
+-------------+--------+-------------------------------------------------+
| | arg1 arg2 |
+-------------+--------+-------------------------------------------------+
| deduce T in | T | enable_if_t<(sizeof(T) > sizeof(float), float> |
| from | char | float |
+-------------+--------+-------------------------------------------------+
Se aplican las reglas normales de deducción de plantillas. El segundo "argumento" es un contexto no deducible, por lo que deducimos T
como char
. sizeof(char) > sizeof(float)
, es falso, y enable_if_t<false, float>
es un tipo no válido, por lo que la deducción de tipos debería fallar ... pero, la falla de la deducción solo puede ocurrir
en el contexto inmediato del tipo de función y sus tipos de parámetros de plantilla
y no estamos tratando con un tipo de función o tipos de parámetros de plantilla de función, estamos tratando con tipos de parámetros de plantilla de clase . Una clase no es una función, por lo que la exclusión de SFINAE no debería aplicarse si tomamos todo literalmente, y el ejemplo de CWG modificado debería conducir a un error grave.
Sin embargo, el espíritu de la regla parece estar más en la línea de:
Solo los tipos y expresiones no válidos en el contexto inmediato del proceso de deducción pueden dar como resultado un error de deducción.
No sé cuál sería la razón para excluir específicamente la deducción de especialización parcial de clase. Además, la ordenación parcial de las plantillas parciales de especializaciones parciales también parecen funciones. Desde [temp.class.order]:
Para las especializaciones parciales de dos plantillas de clase, la primera es más especializada que la segunda si, dado lo siguiente, reescriba dos plantillas de función , [...]
Por lo tanto, el Estándar ya en la siguiente sección muestra una dualidad entre las especializaciones parciales de plantilla de clase y las plantillas de función. El hecho de que esto solo se aplique a la ordenación de especialización parcial, y no al fallo de sustitución durante la deducción del argumento de especialización parcial, me parece un defecto.
† El ejemplo en sí era X<double, float>
. Pero esto en realidad no demuestra ni requiere SFINAE, ya que no habría falla de sustitución en ninguna parte.