c++ - retorno - ¿Deben tratarse las funciones de argumento de la plantilla como potencialmente contradictorias?
tipos de funciones en c (1)
Introducción
template<int F(), int N = F()> void func ();
En esta respuesta, revisaremos paso a paso las secciones relevantes de la Norma Internacional para demostrar que el fragmento de arriba está bien formado.
¿Qué dice el Estándar Internacional ( N3337 )?
El Standardese
14.1p9 Parámetros de la plantilla
[temp.param]
Un argumento de plantilla por defecto es un argumento de plantilla (14.3) especificado después de
=
en un parámetro de plantilla . [...]
14.3p6 Argumentos de plantilla
[temp.arg]
Si el uso de un argumento de plantilla da lugar a una construcción mal formada en la ejemplificación de una especialización de plantilla, el programa está mal formado.
14.3.2p1 Plantilla de argumentos sin tipo de plantilla
[temp.arg.nontype]
Un argumento de plantilla para un parámetro de plantilla que no sea de tipo y sin plantilla será uno de:
- para un parámetro de plantilla sin tipo de tipo integral o de enumeración, una expresión de constante convertida (5.19) del tipo del parámetro de plantilla ; o
- el nombre de un parámetro de plantilla sin tipo; o
- una expresión constante (5.19) que designa la dirección de un objeto [...] ; o
- una expresión constante que se evalúa como un valor de puntero nulo (4.10); o
- una expresión constante que evalúa a un valor de puntero de miembro nulo (4.11); o
- un puntero al miembro expresado como se describe en 5.3.1
Expresiones constantes 5.19p3
[expr.const]
Una expresión de constante literal es una expresión de constante de núcleo prvalue de tipo literal, pero no de tipo puntero. Una expresión de constante integral es una expresión constante literal de tipo de enumeración integral o no codificado. Una expresión constante convertida de tipo
T
es una expresión constante literal, implícitamente convertida al tipoT
, [...]
8.3.6p3 Argumentos predeterminados
[dcl.fct.default]
Un argumento predeterminado se especificará solo en la cláusula de declaración de parámetro de una declaración de función o en un parámetro de plantilla (14.1); en el último caso, la cláusula initializer será una expresión de asignación .
El veredicto
Las secciones anteriores nos hacen llegar a las siguientes conclusiones:
- Un argumento de plantilla predeterminado es un argumento de plantilla , y;
- al crear una instancia de una plantilla, todos los argumentos de la plantilla deben ser utilizables en el contexto donde aparecen, y;
- cada argumento de plantilla para un parámetro de plantilla que no es de tipo y que no aparece en la plantilla que aparece en un programa debe ser una expresión constante literal , y;
- el argumento predeterminado para un parámetro de plantilla será una expresión de asignación .
La explicación
template<int F(), int N = F()>
void func ();
constexpr int (*F)() = <some_initializer>; // (A)
constexpr int N = <explicit_template_argument> OR <F()> // (B)
El fragmento anterior puede usarse como un ayudante mental para facilitar el razonamiento sobre el equivalente de los parámetros de la plantilla , dado un conjunto de argumentos de plantilla .
Para ver si (B) es válido o no, donde no se da un argumento de plantilla explícito para N , debemos evaluar (A) - y la evaluación de (A) puede dar un valor para F que se puede usar en la constante- expresión requerida por (B) .
Con eso dicho; Sí , la plantilla es legal C ++ 11.
Legal
constexpr int g () { ... }
// func<&g>
constexpr int (*F)() = &g; // ok
constexpr int N = F(); // ok
Mal formado
int f () { ... }
// func<&f>
constexpr int (*F)() = &f; // ok
constexpr int N = F(); // ill-formed, not a constant-expression
Prima
El mismo conjunto de reglas se aplica a la siguiente plantilla;
template<int X, int N = 100/X>
void gunc ();
gunc<0> (); // ill-formed, `100/0` is not mathematically defined,
// and is therefore not a constant-expression
Para el abogado de idiomas
Y esto, el uso inútil de un argumento de plantilla predeterminado, es en realidad legal ya que F()
podría ser una expresión constante .
Sin embargo, F()
no puede ser una expresión constante convertida para darle a N un valor, pero esto no ocurre hasta que (si es que lo hace) el argumento predeterminado se utiliza realmente.
template<void F(), int N = F()>
void hunc ();
void f ();
hunc<&f, 10> (); // legal
hunc<&f > (); // ill-formed
Considera este programa:
template <int F(), int N = F()>
void f() { }
constexpr int g() { return 1; }
int main() { f<g>(); }
Es esto valido? ¿Se requiere que los compiladores vean en el momento de la definición de la plantilla, que F
podría referirse a una función constexpr
y, por lo tanto, el argumento predeterminado para N
podría ser válido?
gcc y clang lo aceptan, pero Intel 1 rechaza la función de plantilla en el momento de definición de la plantilla porque F()
no es una expresión constante. Intel acepta f<g, g()>()
si se elimina el argumento predeterminado, por lo que entiende que g()
se puede utilizar en una expresión constante en general.
No está claro para mí lo que dice el estándar. Está claro que (C ++ 11 [expr.const] p2)
una invocación de una función distinta de un constructor
constexpr
para una clase literal o una funciónconstexpr
hace que una expresión no sea constante, pero no me queda claro si eso aplica aquí. En el tiempo de definición de la plantilla, ciertamente parece aplicarse, ya que F
no está declarada como una función constexpr
, pero al mismo tiempo, los errores en el tiempo de definición de la plantilla se deben diagnosticar solo si no hay una posible instanciación válida de la plantilla , y parece que hay una instanciación válida aquí.
Puedo ver los argumentos para ambas respuestas, así que me estoy confundiendo. ¿Hay una respuesta definitiva a esta pregunta?
1. Volver a probar con el lanzamiento actual del compilador de Intel muestra que funciona bien, por lo que, presumiblemente, los desarrolladores de Intel lo consideraron un error y lo arreglaron desde entonces. Esta es una gran sugerencia de que el código debe ser válido. Sin embargo, sería bueno obtener una respuesta concluyente basada en el estándar.