Czy T musi być kompletnym typem do użycia w `std :: declval <T>`?

11

Rozważ ten przykład (pochodzący stąd ):

#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;
}

To kompiluje bez błędów na gcc9.2 ale gcc7.2 i brzękiem 10.0.0 narzekać Bnie jest kompletna. Błąd Clangs to:

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
źródło
1
Tytuł pytania nie pasuje do błędu? Dla mnie wygląda na to, że GCC narzeka .f(). To ma sens; niekompletny typ Bnie ma członka f.
MSalters
@MSalters Myślałem tak samo, ale jaki jest prawdziwy problem? Zakładam, że po otrzymaniu instancji std::declvalnie ma już znaczenia, czy typ był kompletny, czy nie (i chyba się mylę)
idclev 463035818
[expr.ref] / 2 (C ++ 11) mówi o dostępie członków klasy: „Dla pierwszej opcji (kropki) pierwsze wyrażenie powinno mieć pełny typ klasy” . I Bnie jest ani kompletny ani uważany za kompletny w alias-declaration.
Language Lawyer,
@LanguageLawyer Nie znalazłem cytowanego zdania, ale tylko „Typ klasy powinien być kompletny, chyba że dostęp członka klasy pojawia się w definicji tej klasy”
idclev 463035818,
1
@LanguageLawyer ok, więc zgadzam się, że moja interpretacja była wyłączona i wydaje się, że coś się zmieniło od c ++ 11, co sprawia, że ​​powyższe jest poprawne w nowszych standardach, ale nie w c ++ 11. Czy mógłbyś napisać odpowiedź?
idclev 463035818

Odpowiedzi:

9

Źródłem błędu nie jest std::declval, ale niekompletny dostęp członka klasy.

Aż rozdzielczość CWG1836 została połączona 2,5 roku temu, a średnia wymagana klasę być kompletne w wyrażeniu dostępu członkiem klasy ( E1.E2).
[expr.ref] / 2 w C ++ 11 :

W przypadku pierwszej opcji (kropki) pierwsze wyrażenie powinno mieć pełny typ klasy.

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

W przypadku pierwszej opcji (kropki) pierwszym wyrażeniem jest wartość glvalue o pełnym typie klasy.

A klasa alias-declarationsama w sobie nie jest uważana za kompletną member-specification.
[class.mem] / 6 w C ++ 17 :

Klasa jest uważana za całkowicie zdefiniowany typ obiektu ([podstawowy.types]) (lub pełny typ) na końcu }specyfikatora klasy . W ramach specyfikacji członka klasy klasa jest uważana za kompletną w ramach ciał funkcji, domyślnych argumentów, specyfikatorów noexcept i domyślnych inicjatorów elementów (w tym takich rzeczy w klasach zagnieżdżonych). W przeciwnym razie zostanie uznany za niekompletny w ramach specyfikacji członków własnej klasy .

Prawnik językowy
źródło
8

Od [declval] :

Uwagi: Parametr szablonu Tz declvalmoże być niekompletny typ.

To sformułowanie jest obecne od C ++ 11 (więc nie jest możliwe, aby kompilatory były zgodne z wcześniejszym standardem)

AndyG
źródło
niesamowite, na to liczyłem. Wygląda na to, że gcc ma to naprawione, nie
dzwoni
@ previouslyknownas_463035818: Moją pierwszą myślą było, że Tpowinien to być absolutnie pełny typ. Cieszę się, że sprawdziłem standard.
AndyG,