tipos - ¿Tomar la dirección de una variable local es una expresión constante en C++ 11?
variables en c++ (6)
5.19p2 no define expresiones constantes, define expresiones constantes centrales.
Una expresión constante central solo se convierte en una expresión constante si se ajusta a una de las reglas en 5.19p3. Allí, la parte relevante ya fue señalada por jrok:
Una expresión de constante de dirección es una expresión de constante de núcleo de valor nominal de tipo puntero que se evalúa a la dirección de un objeto con duración de almacenamiento estático, a la dirección de una función, a un valor de puntero nulo, o una expresión de constante de núcleo de valor nominal de tipo
std::nullptr_t
.
Su expresión constante de núcleo &y
no se evalúa en ninguno de ellos, por lo que no es una expresión de constante de dirección y , por lo tanto, no es una expresión constante.
El siguiente programa de C ++ 11:
int x = 42;
void f()
{
int y = 43;
static_assert(&x < &y, "foo");
}
int main()
{
f();
}
No compila con gcc 4.7 ya que se queja:
error: ‘&y’ is not a constant expression
Esto estaría de acuerdo con mi intuición. La dirección de y
potencialmente cambia con cada invocación de f
, por lo que, por supuesto, no se puede calcular durante la traducción.
Sin embargo, ninguno de los puntos en 5.19 [expr.const] parece impedir que sea una expresión constante.
Los únicos dos contendientes que veo son:
una conversión de lvalue a rvalue ...
pero, a menos que esté equivocado (?), no hay conversiones de valor a valor en el programa.
Y
una
id-expression
que se refiere a una variable [snip] a menos que:
- Se inicializa con una expresión constante.
que y
es - se inicializa con la expresión constante 43
.
Entonces, ¿es esto un error en el estándar, o me estoy perdiendo algo?
Actualizar:
Es tan confuso como el infierno, pero creo que estoy al tanto de eso, así que déjame mostrarte un ejemplo que mostrará lo que está pasando:
int x = 42;
void f()
{
int y = 43;
// address constant expressions:
constexpr int* px = &x; // OK
constexpr int* py = &y; // ERROR: pointer context for local variable
// boolean constant expressions:
constexpr bool bx = &x; // OK
constexpr bool by = &y; // OK
// comparison constant expressions:
constexpr bool eq = (&x == &y); // OK
constexpr bool lt = (&x < &y); // ERROR: undefined behaviour disqualifies
a constant expression
}
int main()
{
f();
}
Primero, distinga entre una expresión constante de núcleo (5.19p2) y una expresión constante (5.19p4). Las subexpresiones específicas de una expresión constante solo tienen que ser expresiones constantes centrales, no expresiones constantes. Es decir, ser una expresión constante es una propiedad de la expresión completa, no subexpresiones. También requiere mirar el contexto en el que se usa la expresión completa.
Entonces, como resulta que el error gcc es engañoso. En primer lugar, &y
puede ser una expresión constante en algunos contextos. En segundo lugar, la razón por la que &x < &y
no es una expresión constante se debe a la comparación de punteros no relacionados, no a la subexpresión &y
.
En C ++ pre-C ++ 11:
Otras expresiones [que las expresiones constantes integrales] se consideran expresiones constantes solo con el propósito de la inicialización de objetos estáticos no locales (3.6.2). Tales expresiones constantes evaluarán a uno de los siguientes:
[...]
- una expresión de constante de dirección,
[...]
Una expresión de constante de dirección es un puntero a un valor l que designa un objeto de duración de almacenamiento estático, un literal de cadena (2.13.4) o una función.
Dado que y
no tiene una duración de almacenamiento estático, &y
no sería una expresión constante.
C ++ 11 parece haber cambiado esto; Sin embargo, sospecho que esto es un sobrescrito. (C ++ pre-C ++ 11 enumera las cosas que son expresiones constantes. C ++ 11 enumera las cosas que no lo son. Sería fácil que alguien se olvidara).
Independientemente, por supuesto: su comparación no se puede usar en C ++ estándar; los resultados de comparar dos direcciones que no apuntan al mismo objeto no están especificados. (Por otro lado, ocasionalmente he usado algo similar en el código dependiente de la máquina. No estáticamente, pero en plataformas como Linux en PC o Solaris, es posible determinar si un puntero apunta a un objeto con vida útil estática y variable automática, o memoria asignada dinámicamente con tales trucos.)
EDITAR:
El contestador de paxdiablo ha citado el pasaje que no encontré en mi lectura de C ++ 11; C ++ 11 sigue la misma regla que C ++ pre-11 a este respecto, y para ser una expresión de dirección constante, la dirección debe ser la de un objeto con una vida útil estática (o una función, o un puntero nulo).
Intentemos determinar qué requisitos debe cumplir paso a paso la expresión de la declaración static_assert mediante n3485.
[dcl.dcl] / 1
static_assert-declaración:
static_assert (
expresión-constante,
cadena-literal) ;
[dcl.dcl] / 4
En una declaración static_assert, la expresión constante será una expresión constante que se puede convertir contextualmente a
bool
.
[expr.const] / 4
En conjunto, las expresiones constantes literales , las expresiones constantes de referencia y las expresiones constantes de dirección se denominan expresiones constantes .
Entonces, ¿qué tipo de expresión constante es &x < &y
? No es una expresión constante de dirección :
[expr.const] / 4
Una expresión de constante de dirección es una expresión de constante de núcleo prvalue (después de las conversiones requeridas por el contexto) de tipo
std::nullptr_t
o de tipo puntero [...].
El tipo de &x < &y
es bool
según [expr.rel] / 1.
Tampoco es una expresión constante de referencia , por lo que debe ser una expresión constante literal , si la hay .
Una expresión de constante literal es una expresión de constante de núcleo prvalue de tipo literal [...]
Por lo tanto, &x < &y
tiene que cumplir los requisitos de una expresión constante de núcleo .
Como lo señalaron TemplateRex y hvd en los comentarios, en este caso particular, &x < &y
no cumple con los requisitos de una expresión constante del núcleo :
[expr.const] / 2
[una expresión constante de núcleo no debe contener] un operador relacional o de igualdad donde el resultado no está especificado;
[expr.rel] / 2
Si dos punteros
p
yq
del mismo tipo apuntan a objetos diferentes que no son miembros del mismo objeto o elementos de la misma matriz o funciones diferentes, o si solo uno de ellos es nulo, los resultados dep<q
,p>q
,p<=q
,p>=q
no están especificados.
Sin embargo, para un ejemplo como
int arr[2] = {1, 2};
static_assert(&a[0] < &a[1], "");
La expresión a < a+1
cumple este requisito.
Lo siento, estoy de acuerdo en que la respuesta anterior fue probablemente una lectura incorrecta de los elementos. En cambio, la cláusula relevante real es el párrafo 3 de la sección 5.19 [expr.const], que dice (resaltado agregado):
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 constante constante es una expresión constante literal de tipo de enumeración integral o sin ámbito. [Nota: tales expresiones pueden usarse como límites de matriz (8.3.4, 5.3.4), como longitudes de campo de bits (9.6), como inicializadores del enumerador si el tipo subyacente no es fijo (7.2), como constantes de puntero nulas (4.10) ), y como alineaciones (7.6.2). —Endente final] Una expresión constante convertida de tipo T es una expresión constante literal, convertida implícitamente al tipo T, donde se permite la conversión implícita (si existe) en una expresión constante literal y la secuencia de conversión implícita contiene solo conversiones definidas por el usuario, Conversiones de valores a valores (4.1), promociones integrales (4.5) y conversiones integrales (4.7) distintas de las conversiones de reducción (8.5.4). [Nota: tales expresiones se pueden usar como expresiones de caso (6.4.2), como inicializadores del enumerador si el tipo subyacente es fijo (7.2) y como argumentos de plantilla de tipo no integral o de enumeración (14.3). —Endente de nota] Una expresión de constante de referencia es una expresión de constante de núcleo de valor que designa un objeto con una duración de almacenamiento estático o una función. Una expresión de constante de dirección es una expresión de constante de núcleo de valor nominal de tipo puntero que se evalúa a la dirección de un objeto con duración de almacenamiento estático , a la dirección de una función, a un valor de puntero nulo, o una expresión de constante de núcleo de valor nominal de tipo std : nullptr_t. En conjunto, las expresiones constantes literales, las expresiones constantes de referencia y las expresiones constantes de dirección se denominan expresiones constantes.
Una expresión constante central no es directamente una expresión constante, hay condiciones adicionales que se explican en este tercer párrafo.
Sí, te estás perdiendo el hecho de que, mientras que y
sí se inicializa con una expresión constante, no es lo mismo que &y
.
La dirección de y
puede variar mucho dependiendo de su historial de pila de llamadas.
El párrafo 3 de C++11 5.19 Constant expressions
establece las condiciones bajo las cuales el operador address-of puede considerarse una expresión constante (cómo se permite que las expresiones constantes centrales detalladas en el párrafo 2 se conviertan en expresiones constantes "reales"):
... Una expresión de constante de dirección es una expresión de constante de núcleo de valor nominal de tipo puntero que se evalúa a la dirección de un objeto con duración de almacenamiento estático, a la dirección de una función, a un valor de puntero nulo, o una expresión de constante de núcleo de valor predeterminado de escribe std :: nullptr_t. En conjunto, las expresiones constantes literales, las expresiones constantes de referencia y las expresiones constantes de dirección se denominan expresiones constantes.
Dado que &y
no es ninguna de esas cosas, no se considera una expresión constante.
Tomar la dirección de algo no es el culpable aquí, sino la comparación de punteros que utiliza el operator<
en objetos no relacionados.
Los operadores relacionales en punteros solo se especifican para punteros a objetos dentro de la misma clase o dentro de una matriz (5.9 Operadores relacionales [expr.rel], puntos 3 y 4). La comparación relacional para punteros a objetos no relacionados no está especificada.
La comparación de la dirección para la igualdad en lugar de ordenar funciona:
int x = 42;
void f()
{
int y = 43;
static_assert(&x != &y, "foo");
^^ <--- "<" on unrelated objects is unspecified
}
int main()
{
f();
}
Solo para mostrar que esto no tiene nada que ver con const-expresiones per se,
void f()
{
int y[2] = { 42, 43 };
static_assert(&y[0] < &y[1], "foo");
^ <--- "<" on objects within an array is specified
}
int main()
{
f();
}
Otro ejemplo en vivo .