¿T tiene que ser un tipo completo para ser usado en `std :: declval <T>`?

11

Considere este ejemplo (viniendo de aquí ):

#include <type_traits>
#include <iostream>
template <typename U>
struct A {
};

struct B {
   template <typename F = int>
   A<F> f() { return A<F>{}; }

   using default_return_type = decltype(std::declval<B>().f());
};

int main()
{
    B::default_return_type x{};
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
}

Se compila sin errores en gcc9.2 pero gcc7.2 y clang 10.0.0 se quejan de Bno estar completos. El error de Clangs es:

prog.cc:11:58: error: member access into incomplete type 'B'
   using default_return_type = decltype(std::declval<B>().f());
                                                         ^
prog.cc:7:8: note: definition of 'B' is not complete until the closing '}'
struct B {
       ^
prog.cc:16:8: error: no type named 'default_return_type' in 'B'
    B::default_return_type x{};
    ~~~^
prog.cc:17:35: error: no member named 'default_return_type' in 'B'
    std::cout << std::is_same< B::default_return_type, A<int>>::value;
                               ~~~^
idclev 463035818
fuente
1
¿El título de la pregunta no parece coincidir con el error? Para mí, parece que GCC se queja .f(). Eso tiene sentido; el tipo incompleto Bno tiene un miembro f.
MSalters
@MSalters pensé lo mismo, pero ¿cuál es el verdadero problema aquí? Supongo que una vez que obtengas una instancia std::declvalya no importa si el tipo estaba completo o no (y supongo que estoy equivocado con eso)
idclev 463035818
[expr.ref] / 2 (C ++ 11) dice acerca del acceso del miembro de la clase: "Para la primera opción (punto), la primera expresión tendrá un tipo de clase completo" . Y Bno está completo ni se considera completo en alias-declaration.
Law Lawyer
@LanguageLawyer No encontré la oración que cita, pero solo "El tipo de clase se completará a menos que el acceso del miembro de la clase aparezca en la definición de esa clase"
idclev 463035818
1
@LanguageLawyer ok, entonces estoy de acuerdo en que mi interpretación estaba apagada y parece que algo ha cambiado desde c ++ 11, lo que hace que lo anterior esté bien en los estándares más nuevos, pero no en c ++ 11. ¿Te importaría escribir una respuesta?
idclev 463035818

Respuestas:

9

La fuente del error no es el std::declvalacceso incompleto a los miembros de la clase.

Hasta que la resolución de CWG1836 se fusionó hace 2.5 años, el estándar requería que la clase se completara en una expresión de acceso de miembro de clase ( E1.E2).
[expr.ref] / 2 en C ++ 11 :

Para la primera opción (punto), la primera expresión tendrá un tipo de clase completo.

[expr.ref] / 2 en C ++ 17 :

Para la primera opción (punto), la primera expresión será un valor de gl que tenga un tipo de clase completo.

Y una clase no se considera completa en alias-declarationsí misma member-specification.
[class.mem] / 6 en C ++ 17 :

Una clase se considera un tipo de objeto completamente definido ([basic.types]) (o tipo completo) al cierre }del especificador de clase . Dentro de la especificación de miembro de clase , la clase se considera completa dentro de los cuerpos de función, argumentos predeterminados, noexcept-specifier s e inicializadores de miembro predeterminados (incluidas esas cosas en clases anidadas). De lo contrario, se considera incompleto dentro de su propia especificación de miembro de clase .

Abogado de idiomas
fuente
8

De [declive] :

Observaciones: el parámetro Tde plantilla declvalpuede ser de tipo incompleto.

Esta redacción ha estado presente desde C ++ 11 (por lo que no es posible que los compiladores se ajusten a un estándar anterior)

AndyG
fuente
increíble, eso es lo que esperaba. Parece que gcc lo solucionó, no
suena
@ formerlyknownas_463035818: Mi primer pensamiento fue que Tdebería ser absolutamente un tipo completo. Me alegro de haber comprobado el estándar.
AndyG