Muss T ein vollständiger Typ sein, um in `std :: declval <T>` verwendet zu werden?

11

Betrachten Sie dieses Beispiel (von hier ):

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

Es wird ohne Fehler auf gcc9.2 kompiliert, aber gcc7.2 und clang 10.0.0 beschweren sich über die Unvollständigkeit B. Clangs Fehler ist:

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
quelle
1
Der Fragentitel scheint nicht mit dem Fehler übereinzustimmen? Für mich sieht es so aus, als würde sich GCC darüber beschweren .f(). Das macht Sinn; Der unvollständige Typ Bhat kein Mitglied f.
MSalters
@ MSalters dachte ich das gleiche, aber was ist dann das eigentliche Problem hier? Ich würde annehmen, dass std::declvales keine Rolle mehr spielt, ob der Typ vollständig ist oder nicht (und ich glaube, ich bin damit falsch)
idclev 463035818
[expr.ref] / 2 (C ++ 11) sagt über Klassenmitglied Zugang: „Für die erste Option (dot) der erste Ausdruck kompletten Klassentyp haben soll“ . Und Bist weder vollständig noch als vollständig in alias-declaration.
Sprachanwalt
@LanguageLawyer Ich habe den von Ihnen zitierten Satz nicht gefunden, sondern nur "Der Klassentyp muss vollständig sein, es sei denn, der Zugriff auf Klassenmitglieder erscheint in der Definition dieser Klasse"
idclev 463035818
1
@LanguageLawyer ok, dann stimme ich zu, dass meine Interpretation nicht stimmte und es scheint, dass sich seit C ++ 11 etwas geändert hat, was das Obige in neueren Standards in Ordnung macht, aber nicht in C ++ 11. Würde es Ihnen etwas ausmachen, eine Antwort zu schreiben?
idclev 463035818

Antworten:

9

Die Fehlerquelle ist nicht std::declval, sondern unvollständiger Zugriff auf Klassenmitglieder.

Bis zur Auflösung der Auflösung von CWG1836 vor 2,5 Jahren erforderte der Standard, dass die Klasse in einem Zugriffsausdruck für Klassenmitglieder ( E1.E2) vollständig sein muss .
[expr.ref] / 2 in C ++ 11 :

Für die erste Option (Punkt) muss der erste Ausdruck den vollständigen Klassentyp haben.

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

Für die erste Option (Punkt) muss der erste Ausdruck ein Wert mit vollständigem Klassentyp sein.

Und eine Klasse ist nicht als abgeschlossen betrachtet in alias-declarationinnerhalb seiner eigenen member-specification.
[class.mem] / 6 in C ++ 17 :

Eine Klasse wird als ein vollständig definierte Objekttyp ([basic.types]) (oder vollständiger Typen) bei der Schließung }der Klasse-Spezifizierer . Innerhalb der Klasse Mitglied-Spezifikation wird die Klasse als vollständig in Funktionsrümpfe, Standardargumente, angesehen noexcept-Spezifizierer s und Standard - Mitglied initializers (einschließlich solche Dinge in verschachtelten Klassen). Ansonsten wird es als unvollständig in seiner eigenen Klasse betrachtet Mitglied-Spezifikation .

Sprachanwalt
quelle
8

Aus [Deklaration] :

Anmerkungen: Der Vorlagenparameter Tvon declvalkann ein unvollständiger Typ sein.

Dieser Wortlaut ist seit C ++ 11 vorhanden (daher können Compiler nicht einem früheren Standard entsprechen).

AndyG
quelle
Super, darauf habe ich gehofft. Scheint, als hätte gcc es
repariert, klirrte
@ ehemalsknownas_463035818: Mein erster Gedanke war, dass dies Tunbedingt ein vollständiger Typ sein sollte. Ich bin froh, dass ich den Standard überprüft habe.
AndyG