c++ language-lawyer

c++ - Dentro de una clase, ¿por qué `auto b()-> decltype(a()){}` funciona pero `decltype(a()) b(){}` no?



language-lawyer (2)

Algunas adiciones a la respuesta de Brian :

  1. En el primer ejemplo, el a() no se transforma a (*this).a() . Esa transformación se especifica en [class.mfct.non-static]/3 y solo se lleva a cabo "en un contexto donde se puede usar". Sin esta transformación, el código está mal formado por violar [expr.prim.id]/2 :

    Una expresión-id que denota un miembro de datos no estáticos o una función de miembro no estático de una clase solo se puede usar:

    • como parte de un acceso de miembro de clase ([expr.ref]) en el que la expresión de objeto se refiere a la clase 63 del miembro o una clase derivada de esa clase, o

    • para formar un puntero al miembro ([expr.unary.op]), o

    • si esa id-expresión denota un miembro de datos no estáticos y aparece en un operando sin evaluar.

    utilizando la expresión-id a , que denota una función miembro no estática, fuera de los contextos permitidos.

  2. El hecho de que la transformación al acceso de miembros de clase no tenga lugar es importante porque hace que el siguiente código sea válido:

    struct A { int a; decltype(a) b(); };

    Si el decltype(a) anterior se transformara en decltype((*this).a) , entonces el código estaría mal formado.

  3. *this tiene una exención especial de la regla habitual de que el objeto en un acceso de miembro de clase debe tener un tipo completo ( [expr.prim.this]/2 ):

    A diferencia de la expresión de objeto en otros contextos, *this no se requiere que sea de tipo completo para fines de acceso de miembros de clase ([expr.ref]) fuera del cuerpo de la función miembro.

Considere el siguiente código: (Ideone)

struct S { int a() {return 0;} decltype(a()) b() {return 1;} };

Me da el siguiente error:

error: no se puede llamar a la función miembro ''int S :: a ()'' sin objeto


Por otro lado, este código compila bien: (Ideone)

struct S { int a() {return 0;} auto b() -> decltype(a()) {return 1;} };


¿Por qué un ejemplo funciona, pero otro no compila?

¿Es el comportamiento del compilador totalmente correcto en ambos ejemplos?

Si el compilador es correcto, ¿por qué el estándar exige un comportamiento tan extraño?


Como a es una función miembro no estática, a() se interpreta como (*this).a() . Citando en parte de [expr.prim.general] / 3,

Si una declaración declara una función miembro o una plantilla de función miembro de una clase X , la expresión es un prvalor de tipo "puntero a cv-qualifier-seq X " entre el cv-qualifer-seq opcional y el final de la función-definición , miembro-declarador , o declarador . No aparecerá antes del cv-qualifier-sep opcional y no aparecerá dentro de la declaración de una función miembro estática (aunque su tipo y categoría de valor se definen dentro de una función miembro estática, ya que están dentro de una función miembro no estática) .

El tipo trailing-return viene después del cv-qualifier-seq opcional (se omite en sus ejemplos, ya que S::b no está calificado para cv), por this puede aparecer allí, pero no puede aparecer antes.