2011-05-08 14 views
13

(motivada por an answer.)¿Qué es decltype (0 + 0)?

Dada N3290, §7.1.6.2p4, donde los elementos de la lista no están numeradas, pero contados aquí para nuestra conveniencia:

El tipo denotado por decltype (e) se define como sigue:

  1. si e es un id-expresión unparenthesized o un unparenthesized acceso miembro de la clase (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;
  2. de otro modo, si E es un xValue, decltype (e) es T & &, donde T es el tipo de e;
  3. de lo contrario, si e es un lvalue, decltype (e) es T &, donde T es el tipo de e;
  4. de lo contrario, decltype (e) es el tipo de e.

¿Cuál es el tipo especificado por decltype (0 + 0)?

El elemento 1 no se aplica, 2 podría, pero si no, entonces 3 no se aplica y 4 sería el resultado. Entonces, ¿qué es un valor x, y es 0 + 0 un valor x?

§3.10p1:

Un xValue (un “expira” valor) también se refiere a un objeto, por lo general cerca del final de su vida útil (de modo que sus recursos se pueden mover, por ejemplo). Un xvalor es el resultado de ciertos tipos de expresiones que implican referencias de valores (8.3.2).

No veo nada en §8.3.2 que sería útil aquí, pero sé que "0 + 0" no implica ninguna referencia rvalue. El literal 0 es un valor pr, que es "un valor r que no es un valor x" (§3.10p1). Creo que "0 + 0" también es un valor prve. Si eso es cierto, "decltype (0 + 0)" sería int (no int & &).

¿He perdido algo en mi interpretación? ¿Este código está bien formado?

decltype(0 + 0) x; // Not initialized. 

El código se compila en GCC 4.7.0 20110427 y Clang 2.9 (tronco 126116). No estaría bien formado si el tipo de declty especificó un tipo int & &, por ejemplo.

+3

No veo nada de malo en su razonamiento. Creo que 'decltype (0 + 0)' también debería ser 'int'. –

+1

FWIW, una buena respuesta afirmativa sería ampliar la definición de xvalue y mostrar lo que puede y no puede ser. (Me parece que * muy * útil.) Necesito ver cómo volver a redactar para centrarse en "¿qué es un valor x?" mientras aún se considera este caso concreto de "0 + 0". –

+3

pesar de que el último borrador hace especificar el category_ _value de muchas expresiones, incluyendo cosas como la subasta de sufijo y una nota de 3,10 indica que la cláusula 5 debe mostrar la categoría del valor para cada operador incorporado, no parece el proyecto de mencionar una categoría de valor para cualquiera de los operadores binarios de 5.6 a 5.15 a menos que mis poderes de búsqueda me hayan fallado. –

Respuesta

2

Desde 5.19 [expr.const], cada literal expresión constante es una prvalue .

A expresión constante literal es un núcleo de expresión constante prvalue de tipo literal, pero no Tipo de puntero. Una expresión constante integral es una expresión constante literal del tipo de enumeración integral o no codificado.

Por tanto la regla 4 se aplica a todas las expresiones constantes literales.

+0

Gracias, aunque todavía veo un agujero en la semántica (como lo señaló Charles en un comentario sobre la pregunta) para "int n = 42; decltype (0 + n)". :( –

+0

@Fred: Y, por supuesto, aunque el estándar deja en claro que '0 + 0' es una * expresión constante del núcleo *, su estado * prvalue * permanece en duda. Obviamente, si no fuera un * prvalue * . no podría ser una expresión constante * * integral y un montón de cosas romperían –

+2

tiene esto al revés: el mismo es ** ** definir el término * expresión constante literal * –

0

CCG dice int -

Código:

#include <iostream> 
#include <typeinfo> 

int 
main() 
{ 
    int n; 
    decltype(0 + 0) x; 
    std::cout << "Type of `n': " << typeid(n).name() << std::endl; 
    std::cout << "Type of `x': " << typeid(x).name() << std::endl; 
} 

Salida:

i

i

Editar: Tiene sentido según el punto 4, pero no puedo decir con certeza que el punto 2 en realidad no sea el que está en efecto.Por lo que puedo decir, 0 + 0 se evalúa a 0, y el tipo de 0 es int, por lo que ese es el tipo declarado.

+1

Pero dice lo mismo para int && que para int: http: // ideone .com/6c6N6 –

+0

Eso es verdad. Tenga en cuenta que con GCC, int && es lo mismo que int. Después de todo, una referencia a int es un int. Después de todo, una referencia es solo un alias en teoría. Pruebe 'if (typeid (int) == typeid (int &&)) {std :: cout <<" int == int && "<< std :: endl; } ' Incluso si las cadenas para ellos son las mismas, el tipo de letra real no sería el mismo a menos que sean tratados EXACTAMENTE de la misma manera. Esto me hace preguntarme qué está pasando debajo de todo ... Por ahora, diría que 'decltype (0 + 0)' da como resultado el tipo 'int'. –

2

Su razonamiento es correcto. Una expresión que implica solo constantes es una constante en sí misma. Por lo tanto

decltype(0 + 0) x; 

es igual a

decltype(0) x; 

que es igual a

int x; 
+1

Esto no responde la pregunta. Usar ceros es un marcador de posición, y una buena respuesta mostrará cómo extrapolar a decltype (some_int + another_int) (o mostrar por qué los literales cero son especiales). –

9

0 + 0 es una expresión de dos prvalues, (n3290 par. 3.10) que aplica la incorporada en el operator+, que, por 13.6/12 es LR operator+(L,R), que es una función que devuelve algo que no es una referencia. El resultado de la expresión es por lo tanto también un valor pr (como en 3.10).

Por lo tanto, el resultado de 0 + 0 es un prvalue, 0 es un int, por lo tanto el resultado de 0 + 0 es un int

+2

§13.6p9 es operador * unario *. Quiere §13.6p12. Sin embargo, el borrador dice: "Estas funciones candidatas participan en el proceso de resolución de sobrecarga del operador como se describe en 13.3.1.2 y no se utilizan para ningún otro propósito". (§13.6p1) Creo que "ningún otro propósito" significa que no podemos usarlos para determinar la categoría de valor. +1 para una buena respuesta de todos modos. –

+0

@FredNurk - Edité las referencias. Debo estar cansado ... – rlc

+3

los operadores incorporados no son llamadas de función. Así que no puede tomar 13.6/12 y obtener el tipo de devolución descrito de esos candidatos y aplicarlos a la cláusula 5. Los candidatos de 13.6 solo están activos y relevantes cuando se sobrecarga la resolución (si al menos un operando es de clase o tipo de enumeración)) Y solo se usan para convertir operandos de tipo de clase. Una vez hecho esto, el control se devuelve completamente a la cláusula 5. Ese "operador LR +" no es una función real que de alguna manera se llame. –

5

En definitiva, es un int:

#include <iostream> 
#include <typeinfo> 

template<typename T> 
struct ref_depth 
{ 
     enum { value = 0 }; 
}; 

template<typename T> 
struct ref_depth<T&> 
{ 
     enum { value = 1 }; 
}; 

template<typename T> 
struct ref_depth<T&&> 
{ 
     enum { value = 2 }; 
}; 

int main() { 

    std::cout 
    << "int: " << typeid(int).name() << "\n" 
     "decltype(0 + 0): " << typeid(decltype(0 + 0)).name() << "\n" 
     "int&&: " << typeid(int&&).name() << "\n"; 
    std::cout 
    << "ref_depth: int: " << ref_depth<int>::value << "\n" 
     "ref_depth: decltype(0 + 0): " << ref_depth<decltype(0 + 0)>::value << "\n" 
     "ref_depth: int&&: " << ref_depth<int&&>::value << "\n"; 

} 

de salida:

int: i 
decltype(0 + 0): i 
int&&: i 
ref_depth: int: 0 
ref_depth: decltype(0 + 0): 0 
ref_depth: int&&: 2 
+2

¿Podría incluir el código en la respuesta? (En lugar de resumir la conclusión del código en la respuesta y vincularlo a un sitio completamente diferente) –

+1

@Fred Nurk: Me he tomado la libertad. –