decltype () variadic Vorlage Basisklasse

8

Ich habe den folgenden Code, wo ich decltype() nicht auf Derived -Klasse arbeiten soll, um run() Basisklassenmethode return-type zu erhalten, da die Basisklasse keinen Standardkonstruktor hat.

%Vor%

Ich weiß, dass Sie declval<> verwenden können, um Member-Funktionen zu erhalten, ohne Konstruktoren zu durchlaufen, aber meine Frage ist, warum decltype hier funktioniert. Ich habe versucht, im C ++ Standard etwas Relevantes zu finden, habe aber nichts gefunden. Probieren auch mehrere Compiler (gcc 5.2, 7.1 und clang 3.8) und haben das gleiche Verhalten.

    
AdvSphere 01.02.2018, 00:47
quelle

2 Antworten

12
Sieh dir die Magie unbewerteter Zusammenhänge an ... und lüge.

Eigentlich versucht man so etwas wie:

%Vor%

wird ein Kompilierungsfehler sein. Es ist ein Compilerfehler, weil wir bei der Auswertung von Derived::Derived() Base::Base() aufrufen müssen, was nicht existiert.

Aber das ist ein Detail der Implementierung des Konstruktors. In einem evaluierten Kontext müssen wir das sicherlich wissen. Aber in einem nicht evaluierten Kontext müssen wir nicht so weit gehen. Wenn Sie std::is_constructible<Derived>::value untersuchen, würden Sie sehen, dass true ! Dies liegt daran, dass Sie diesen Konstruktor ohne Argumente instanziieren können, da die Implementierung dieses Konstruktors außerhalb des unmittelbaren Kontexts dieser Instanziierung liegt. Diese Lüge - dass Sie können default-construct Derived - erlaubt es Ihnen, Derived{} in diesem Kontext zu verwenden, und der Compiler wird es Ihnen glücklicherweise erlauben, auf Ihren lustigen Weg zu gehen und zu sehen, dass decltype(Derived{}.run()) ist int (was auch nicht beinhaltet, dass run tatsächlich aufgerufen wird, so dass der Rumpf dieser Funktion ebenfalls irrelevant ist).

Wenn Sie im Konstruktor Derived ehrlich waren:

%Vor%

Dann könnte decltype(Derived{}.run()) nicht kompiliert werden, weil jetzt Derived{} selbst in einem nicht evaluierten Kontext schlecht gebildet ist.

Es ist gut, den Compiler nicht zu belügen.

    
Barry 01.02.2018, 00:56
quelle
5

Wenn ein Ausdruck in decltype eine Funktionsvorlage enthält, prüft der Compiler nur die Signatur der Vorlagenfunktion, um festzustellen, ob die Vorlage instanziiert werden konnte, wenn der Ausdruck tatsächlich in einem ausgewerteten Kontext war. Die eigentliche Definition der Funktion wird an dieser Stelle nicht verwendet.

(In der Tat kann std::declval in decltype verwendet werden, obwohl std::declval überhaupt keine Definition hat.)

Ihr Vorlagenkonstruktor hat dieselbe Signatur wie einfach deklariert, aber noch nicht definiert:

%Vor%

Bei der Verarbeitung von decltype betrachtet der Compiler nur so viele Informationen und entscheidet, dass Derived{} ein gültiger Ausdruck ist, ein rvalue vom Typ Derived<> . Der : Base{args...} -Teil ist Teil der Template-Definition und wird nicht in decltype verwendet.

Wenn Sie dort einen Compilerfehler haben möchten, könnten Sie etwas verwenden, um Ihren Konstruktor "SFINAE-freundlicher" zu machen, was bedeutet, dass Informationen darüber, ob eine Spezialisierung des Templates tatsächlich gültig ist oder nicht, in die Templatesignatur eingetragen werden.

%Vor%

Vielleicht möchten Sie auch den Konstruktor ändern, um "zu perfektes Weiterleiten" zu vermeiden. Wenn Sie Derived x; Derived y{x}; verwenden, ist die Template-Spezialisierung Derived(Derived&); besser als die implizite Derived(const Derived&); , und Sie werden versuchen, x an Base{x} zu übergeben, anstatt den impliziten Kopierkonstruktor von% zu verwenden. co_de%.

    
aschepler 01.02.2018 01:04
quelle

Tags und Links