ejemplo - Tipo de(x) en C++
auto en c (5)
§ 7.1.6.4 [dcl.spec.auto] (borrador n3797)
- ... Si el marcador de posición es el especificador de tipo decltype (auto), el tipo declarado de la variable o el tipo de retorno de la función será el marcador de posición solo. El tipo deducido para la variable o tipo de retorno se determina como se describe en 7.1.6.2, como si el inicializador hubiera sido el operando del decltype.
§ 7.1.6.2 [dcl.type.simple]
- Para una expresión e, el tipo denotado por decltype (e) se define de la siguiente manera:
- si e es una expresión-id sin paréntesis o un acceso de miembro de clase sin paréntesis (5.2.5), decltype (e) es el tipo de la entidad nombrada por e. Si no existe tal entidad, o si e nombra un conjunto de funciones sobrecargadas, el programa está mal formado;
- de lo contrario, si e es un xvalor, decltype (e) es T &&, donde T es el tipo de e;
- de lo contrario, si e es un lvalor, decltype (e) es T &, donde T es el tipo de e;
- De lo contrario, decltype (e) es el tipo de e
x
es una expresión-expresión sin paréntesis y, por lo tanto, el tipo de retorno se deduce como el tipo de x
: int
(x)
no es una expresión-id entre paréntesis para que la regla no se aplique. Sin embargo, es una expresión de valor l (entre paréntesis). Por lo tanto, el tipo deducido es T&
donde T
es el tipo de x
: int&
Esta pregunta ya tiene una respuesta aquí:
- Decltype y paréntesis 3 respuestas
Dado:
decltype(auto) f1()
{
int x = 0;
return x; // decltype(x) is int, so f1 returns int
}
decltype(auto) f2()
{
int x = 0;
return (x); // decltype((x)) is int&, so f2 returns int&
}
(Tomado de la moderna C ++ efectiva de Scott Meyer).
Ahora, si he encontrado el párrafo correcto, la Sección 7.1.5.2 Especificadores de tipo simple [dcl.type.simple] del estándar C ++ dice:
Si e es una expresión de id o un acceso de miembro de clase (5.2.5 [expr.ref]), decltype (e) se define como el tipo de la entidad nombrada por e
y el ejemplo de esa sección es:
struct A { double x; }
const A* a = new A();
decltype((a->x)); // type is const double&
Ahora, me pregunto por qué se decltype((x))
el decltype((x))
como int&
en el libro.
Como @Columbo ya ha señalado, (a-> x) debe tomarse como una expresión, y esa expresión está const
, porque * a es const.
Consideremos esto
decltype((a->x));
no fue const
calificado.
Entonces, por ejemplo, esto
decltype((a->x)) ref = (a->x);
de forma implícita, elevaría el * un calificador de const, porque * a''s x sería mutable a través de ref
.
La cita de las normas relevantes es:
N4140 [dcl.type.simple]/4:
Para una expresióne
, el tipo indicado pordecltype(e)
se define de la siguiente manera:
- si e es una expresión-id sin paréntesis o un acceso de miembro de clase sin paréntesis (5.2.5),
decltype(e)
es el tipo de la entidad nombrada pore
. Si no existe tal entidad, o si e nombra un conjunto de funciones sobrecargadas, el programa está mal formado;- de lo contrario, si
e
es undecltype(e)
,decltype(e)
esT&&
, dondeT
es el tipo de e;- de lo contrario, si
e
es undecltype(e)
,decltype(e)
esT&
, dondeT
es el tipo de e ;- de lo contrario,
decltype(e)
es el tipo dee
.
Como x
es un lvalor y la expresión está entre paréntesis, se usa la tercera regla, por lo que decltype((x))
es int&
.
[dcl.spec.auto] / 7 indica que, en términos generales, el tipo de retorno se obtiene aplicando decltype
a la expresión en la declaración de return
. Por lo tanto, como sugiere el comentario, estamos buscando decltype((x))
. Una regla diabólica interviene en:
Tenga en cuenta que el primer punto no se aplica ya que la expresión está entre paréntesis. Por lo tanto obtenemos int&
.
Esta distinción entre aplicaciones de decltype
en identificadores lisos y paréntesis se introdujo con la revisión 6 del documento correspondiente. Ver en la revisión 5 , §2.3:
El tipo indicado por
decltype(e)
se define de la siguiente manera:
- Si
e
tiene la forma(e1)
,decltype(e)
se define comodecltype(e1)
.
La justificación detrás de esto es, presumiblemente, la siguiente: al escribir (x)
, el programador pretende que decltype
no trate al operando como un nombre sino como una expresión, considerando su categoría de valor.
decltype
devuelve el tipo declarado de una variable, o el "tipo" de una expresión (con alguna referencia agregada para indicar el valor de l / r).
Esto permite que sea utilizado para dos propósitos diferentes. A veces esto causa confusión, pero es lo que es.
El token x
es una variable. El tipo de la variable es int
.
Los tokens (x)
no son una variable, sino una expresión (realmente trivial) que no contiene más que una variable. Como tal, el tipo de expresión (determinado por decltype) (x)
es int&
.
El tipo de expresión x
(si pudiera convencer a decltype para que se la diera; no puede) también es int&
, pero la regla de que decltype(ACTUAL_VAR_NAME)
evalúa según el tipo de la variable "gana".
Ahora, nada de lo anterior es cierto. La verdad real es una cita de la norma que describe los pasos que un compilador debe seguir para determinar qué tipo devuelve decltype
. Pero es una mentira efectiva, y una (si la redacción estándar resultó tener errores) que podría indicar que la norma tiene un error cuando no está de acuerdo con ella.