3

Consider the following code:

class A {};

class B {
  using A = A;
};

With Clang and ICC this compiles fine, while GCC fails with the following message:

error: declaration of ‘using A = class A’ changes meaning of ‘A’ [-fpermissive]
    4 |   using A = A;
      |         ^

Question: Which compiler is correct and why?

Seriously
  • 884
  • 1
  • 11
  • 25

1 Answers1

1

Clang and ICC have it right.

[basic.scope.pdecl]

3 ... The point of declaration of an alias or alias template immediately follows the defining-type-id to which the alias refers.

Where the grammar for an alias declaration is

[dcl.pre]

1 ...

alias-declaration:
using identifier attribute-specifier-seqopt = defining-type-id ;

So the "new" meaning of A isn't officially declared until after the part of the declaration that still refers to the "old" A.

Now, that's not to say there is no potential for problems here. For we also have

[basic.scope.class]

2 A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

Had you written:

class A {};

class B {
  A a;
  using A = A;
};

We now have A referring to different declarations between its first use, and the complete class scope. Sure, it's the same type being named, but via different declatation. That's enough to make the code ill-formed NDR. And GCC likely has a heuristic to protect code from this. Which is being tripped by your benign example.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • I've closed the question as a duplicate. It's interesting, since there are multiple linked posts about redeclaring types, and there are different interpretations of whether the code is ok or not, sometimes on the same question. – cigien Mar 02 '21 at 13:15
  • @cigien - I suppose it's down to whther or not the "first use" in the defining-type-id should be enough to violate p2. I don't believe it is. The meaning of A only changes while there is still no ambiguity. And it isn't like the alias declaration is "re-parsed" in the complete class scope. This isn't part of the class of errors p2 is meant to ward against. – StoryTeller - Unslander Monica Mar 02 '21 at 13:25
  • And if GCC's interpretation is indeed the intended one. That seems a bit much to me. But it's not like I'd advocate for such code anyway. – StoryTeller - Unslander Monica Mar 02 '21 at 13:28
  • Sure, this isn't code one *should* write, but for [language-lawyer] questions we don't have to worry about that ;) But [basic/scope/class/#2] is quite interesting. It refers to names in general, not just types, and I've seen different answers making different claims about the validity of the code doing that (also compilers disagree in lots of cases). I've been meaning to make a canonical about that bullet point specifically but haven't gotten around to it yet :( – cigien Mar 02 '21 at 13:33
  • @cigien - I doubt a canonical is possible. It's subject to interplay with various point-of-declaration clauses and class layouts. To covet *all* would likely be closed as too-broad, even when self-answered. Not to mention the whole ill-formed NDR business makes it hard to argue when opinions differ. You see it with questions that touch [temp.res]p8 as well. – StoryTeller - Unslander Monica Mar 02 '21 at 13:38
  • Hmm, yeah, that would be a bit broad. And it wouldn't even be self-answered, since I don't really have a good answer for this. I'll think about it a bit more, but you're probably right. – cigien Mar 02 '21 at 13:43
  • I think [recently merged wording from P1787](http://eel.is/c++draft/class.member.lookup#6) supports GCC's rejection of the OP's example: the declaration set produced by lookup for `A` from `using A = ` differs from the set produced by the lookup for `A` in `B` from immediately after `}` of `B`. The first finds `class A`, the second — the _alias-declaration_ `B::A`. – Language Lawyer Mar 02 '21 at 17:17
  • @LanguageLawyer - I may be misunderstanding, but that appears like a procedure and a lookup set for class members. It can't find `class A` (no associated sub-object). – StoryTeller - Unslander Monica Mar 02 '21 at 17:25
  • Indeed, looks like [class.member.lookup] can't find non-members. But then it produces an empty declaration set, which is still different from the set produced by lookup for `A` in `B` from immediately after _class-specifier_ of `B`. – Language Lawyer Mar 02 '21 at 17:50
  • @LanguageLawyer - While true, it's still before the alias' point of of declaration. Put another way, we'd get an empty set if we searched right before the `using`. And that's not a problem (otherwise, *any* declaration would trip this, since the set is empty prior to it). So searching when we hit `using A =` (without the defining-type-id) should be fine too. `A` is not declared *yet*. – StoryTeller - Unslander Monica Mar 02 '21 at 17:57
  • I don't have a strong opinion on which code with declaration «changing meaning» should be IFNDR and which not. But your last comment makes me think you didn't get which `A` do we look for (or I was not clear enough). In `using A = B;`, `A` is not looked up at all, only `B` is (`B` may be the same identifier as `A`). When saying «search from `using A = `», I meant search from program point P of the _defining-type-id_ (which is after `=`), not search for the _identifier_ `A` immediately after `using`. – Language Lawyer Mar 03 '21 at 02:44
  • @LanguageLawyer I think the first lookup for `S(A,B)` from the program point at `using A = ` is empty. According to [basic.lookup.unqual#2](http://eel.is/c++draft/basic.lookup.unqual#2), since `S(A,B)` is empty, the lookup continue to find `A` in the parent scope of `B`, then it finds `struct A{};`. Conversely, the lookup for `S(A,B)` in complete-class context of B finds `{using A = A;}`, hence, they're different(the first is empty, the second time finds alias-declaration in B). – xmh0511 Mar 04 '21 at 09:40
  • @jackX Isn't this what I wrote in [this comment](https://stackoverflow.com/questions/66438201/c-aliasing-a-type-with-the-same-name/66439799?noredirect=1#comment117465511_66439799)? (Except that I ignored the continuation of lookup as non-relevant to the IFNDR problem) – Language Lawyer Mar 04 '21 at 17:57
  • @LanguageLawyer Yes, that is. I just strong your opinions. – xmh0511 Mar 05 '21 at 01:35