2016-06-06 15 views
8

ich zufällig bemerkt, dass dieser Code kompiliert und funktioniert einwandfrei:Warum ist decltype (Klasse :: Klasse :: class :: member) gültig

struct M { int some_int; }; 
static_assert(std::is_same< 
        decltype(M::M::M::M::some_int) /* <- this */, 
        int>::value, "Types must be int"); 

Warum dies richtig ist (decltype(M::M::M::M::some_int) <=> decltype(M::some_int))?

Welche anderen Konstrukte kann man dieses Muster mit class::class::...::member verwenden?

Compiler: Microsoft (R) C/C++ Optimizing Compiler Version 19.00.23824.1 für x86

+1

Oh Junge .. die upvotes fließen für etwas, das ist offensichtlich ein [duplizieren] (http://stackoverflow.com/q/12135498). –

Antwort

9

Dies funktioniert, weil der injizierten-class-namen:

(N3337) [class]/2: A-Klasse-Namen in den Rahmen eingesetzt ist, in dem es sofort erklärt wird, nachdem der Klasse-Name ist gesehen. Der Klassenname wird auch in den Gültigkeitsbereich der Klasse selbst eingefügt; Dies wird als der Name der injected-Klasse bezeichnet. Zum Zwecke der Zugriffsprüfung wird der Name der injected-Klasse so behandelt, als wäre er ein öffentlicher Membername. [...]

So können Sie beliebig Nest diese, und sie werden auch mit abgeleiteten Typen arbeiten:

struct A { using type = int; }; 
struct B : public A {}; 

using foo = B::B::B::A::A::A::type; 

Beachten Sie, dass im Falle von A[::A]*::A, die injizierte-class-name kann in Betracht gezogen werden, um den Konstruktor zu nennen statt:

[class.qual]/2: in einer Lookup, in dem der Konstruktor ist ein akzeptables Ergebnis Lookup und der verschachtelte-name-specifier kürt eine Klasse C:

- wenn der Name nach dem nested-name-Spezifizierer angegeben wird, wenn in C nachgeschlagen wird der eingespritzte-class-name von C (Ziffer 9) oder

- [...]

Der Name wird stattdessen als Konstruktor der Klasse C bezeichnet.

+2

* "Beachten Sie, dass im Falle von' A [:: A] * :: A 'der Name der injected-Klasse stattdessen als Konstruktor betrachtet werden kann: "* Ib4 der natürliche Folgetüpel: Ja, clang neigt dazu, das falsch zu verstehen. Siehe z.B. [this] (https://stackoverflow.com/questions/29681449/program-being-compiled-differently-in-3-major-c-compilers-which-one-is-right). –

10

Diese in allen Kontexten gültig ist, nicht nur decltype. Eine Klasse enthält einen eigenen Namen als Name der injizierten Klasse. Also innerhalb einer Klasse A::B::M wird der Name M eingegeben, um auf die Klasse A::B::M zu verweisen. Dies bedeutet, dass Sie dann M::M::M::some_member verwenden können, um auf Mitglieder dieser Klasse zu verweisen, wenn Sie dies wirklich möchten.

[Live example]

Beachten Sie, dass, wenn nur auf den Klassennamen beziehen sich (zum Beispiel M::M::M), ist die Situation etwas anders. Wenn ein solcher Verweis an einer Stelle auftritt, an der auch ein Verweis auf eine Funktion möglicherweise korrekt ist, wird stattdessen die Syntax verwendet, um auf den Konstruktor zu verweisen. In Nur-Typ-Kontexten ist jedoch auch eine solche Referenz gültig. Beispiel:

M::M::M m; // illegal, M::M interpreted as reference to constructor 

struct D : public M::M::M // legal, a function could not be references here, so M::M::M means M 
{};