2017-01-06 1 views
8

Ich möchte eine Struktur eine Art Alias ​​auf einen anderen Typ für metaprogramming Zwecke enthalten:Darf ich einen Alias ​​für einen Membertyp für einen Typ in einem umgebenden Bereich mit demselben Namen deklarieren?

struct Foo {}; 

struct WithNestedTypeAlias { 
    using Foo = Foo; 
}; 

Dann kann ich Sachen wie WithNestedTypeAlias::Foo in einer Vorlage tun usw.

Wie ich verstehe, ist diese Art alias gültig weil es die Bedeutung des Foo Typs nicht ändert. Clang kompiliert das glücklich.

jedoch GCC klagt:

test-shadow-alias.cpp:4:20: error: declaration of ‘using Foo = struct Foo’ [-fpermissive] 
    using Foo = Foo; 
        ^
test-shadow-alias.cpp:1:8: error: changes meaning of ‘Foo’ from ‘struct Foo’ [-fpermissive] 
struct Foo {}; 
     ^

Jetzt bin ich verwirrt, weil ich ausdrücklich nicht die Bedeutung von Foo von struct Foo ändern.

Was ist das richtige Verhalten für C++ 14? Ich weiß, dass ich das umgehen kann, indem ich die struct Foo umbenenne, aber ich würde gerne verstehen, ob der Fehler von GCC hier richtig ist.

Hinweise:

+1

Ändern der Anweisung zu 'mit Foo = :: Foo;' behebt es, aber ich kann nicht erklären, warum. –

+0

Ändern zu 'mit Foo = struct Foo;' behebt es auch. Beachten Sie, dass das Äquivalent 'typedef struct Foo Foo;' idiomatischer C-Code ist. – Oktalist

Antwort

7

Die Regel GCC Durchsetzung ist in [basic.scope.class]:

2) Ein Name N in einer Klasse S eingesetzt ist in allen ihren Zusammenhang auf die gleiche Erklärung beziehen und wenn re -bewertet im abgeschlossenen Umfang von S. Keine Diagnose ist für einen Verstoß gegen diese Regel erforderlich.

Die Norm sagt verstößt dies keine Diagnose erfordert, ist es so möglich, dass sowohl GCC und Clang konform sind, da (wenn GCC Recht ist) der Code nicht gültig ist, aber der Compiler nicht erforderlich ist, zu diagnostizieren es.

Der Zweck dieser Regel ist, dass Namen, die in einer Klasse verwendet werden, immer die gleiche Sache bedeuten, und das Neuordnen von Mitgliedern ändert nicht, wie sie interpretiert werden, z.

struct N { }; 

struct S { 
    int array[sizeof(N)]; 

    struct N { char buf[100]; }; 
}; 

In diesem Beispiel werden die Namen N Änderungen bedeutet, und die Mitglieder Neuordnungs würde die Größe von S::array ändern. Wenn S::array definiert ist, bezieht sich N auf den Typ ::N, aber im vollständigen Umfang von S bezieht es sich stattdessen auf S::N. Dies verstößt gegen die oben genannte Regel.

In Ihrem Beispiel den Namen Foo Änderungen in eine weit weniger gefährlich Art und Weise, weil es immer noch auf die gleiche Art bezieht, sprechen jedoch streng tut es Änderung an die Erklärung von S::Foo auf die Erklärung von ::Foo von verweisen. Die Regel ist so formuliert, dass sie sich auf Erklärungen bezieht, daher denke ich, dass der GCC richtig ist.

+0

Hmm, diese Regel macht Sinn. Aber das scheint zu implizieren, dass ich den von François Andrieux in einem Kommentar erwähnten Trick "using Foo = :: Foo" nicht verwenden sollte? Während das momentan funktioniert, ist es immer noch eine neue Deklaration von 'Foo' und somit in Verletzung deiner zitierten Regel, oder? Das würde mich mit der einzigen Lösung "struct AnotherFoo {}; struct Nested {mit Foo = AnotherFoo; }; Verwenden von Foo = AnotherFoo; 'um die verschachtelte' Foo' Deklaration portabel zu machen. – amon

+3

@amon: Das Problem in Ihrem ursprünglichen Code ist, dass das 'Foo' auf der rechten Seite die Bedeutung ändert, weil es nicht qualifiziert ist. Vor der "using" -Deklaration hätte es den globalen Foo gemeint, später hätte es den Alias ​​gemeint. Wenn es qualifiziert ist, dann ist das kein Problem mehr. –

+2

@amon Beachten Sie auch [CWG 42] (http://wg21.link/cwg42), dass Ihre Frage zu 'Foo',' :: Foo' und allem beantworten wird. – bogdan

Verwandte Themen