This prints 1, thinking that 'foo' means a::foo: #include <stdio.h> struct b { typedef struct a foo; int x() { return 1; } void go(); }; struct a : public b { typedef b foo; int x() { return 2; } }; void b::go() { printf("%i\n", ((a *)0)->foo::x()); } int main() { b().go(); } If I trivially parametrise b on the derived type T and turn the cast to a * into a cast to T *, it starts printing 2 as it starts finding the foo from local name lookup: #include <stdio.h> template <typename T> struct b { typedef struct a foo; int x() { return 1; } void go(); }; struct a : public b<a> { typedef b<a> foo; int x() { return 2; } }; template <typename T> void b<T>::go() { printf("%i\n", ((T *)0)->foo::x()); } int main() { b<a>().go(); } This happens with every version of gcc I've tried (4.1 through 4.5). Other compilers error on this code, complaining that they find different foo doing name lookup in a or T and doing a normal local name lookup. I hear the standard agrees with that behaviour.
I think the code should be rejected. Here is what Comeau online says: "ComeauTest.c", line 14: error: ambiguous class member reference -- type "b::foo" (declared at line 4) used in preference to type "a::foo" (declared at line 10)
((a *)0)->foo::x() ((T *)0)->foo::x() Both are those are undefined because you are calling a method of a NULL pointer. (In reply to Andrew Pinski from comment #1) > I think the code should be rejected. Here is what Comeau online says: > "ComeauTest.c", line 14: error: ambiguous class member reference -- type > "b::foo" > (declared at line 4) used in preference to type "a::foo" (declared > at line 10) And clang rejects this: <source>:14:41: error: lookup of 'foo' in member access expression is ambiguous void b::go() { printf("%i\n", ((a *)0)->foo::x()); } ^ <source>:10:13: note: lookup in the object type 'a' refers here typedef b foo; ^ <source>:4:20: note: lookup from the current scope refers here typedef struct a foo; ^
On Sat, Jul 24, 2021 at 6:45 AM pinskia at gcc dot gnu.org <gcc-bugzilla@gcc.gnu.org> wrote: > > https://gcc.gnu.org/bugzilla/show_bug.cgi?id=46283 > > --- Comment #2 from Andrew Pinski <pinskia at gcc dot gnu.org> --- > ((a *)0)->foo::x() > > ((T *)0)->foo::x() > > Both are those are undefined because you are calling a method of a NULL > pointer. That's true. In both cases, one can modify this code, for instance by writing (new a)->foo::x() or (new T)->foo::x(), or by statically instantiating an object of the appropriate type, without changing the relevant name lookup behaviour. > (In reply to Andrew Pinski from comment #1) > > I think the code should be rejected. Here is what Comeau online says: > > "ComeauTest.c", line 14: error: ambiguous class member reference -- type > > "b::foo" > > (declared at line 4) used in preference to type "a::foo" (declared > > at line 10) > > And clang rejects this: > <source>:14:41: error: lookup of 'foo' in member access expression is ambiguous > void b::go() { printf("%i\n", ((a *)0)->foo::x()); } > ^ > <source>:10:13: note: lookup in the object type 'a' refers here > typedef b foo; > ^ > <source>:4:20: note: lookup from the current scope refers here > typedef struct a foo; > ^ It appears (through playing with Matt Godbolt's Compiler Explorer) that a change was made between clang 5.0.1 and clang 6.0.0 that made clang match gcc's behaviour. 5.0.1 gives the errors above, while 6.0.0 compiles the code without warnings or errors.
So the problem is which foo is the right foo here should it be looked up not based on a-> but on the current class. I think the P1787R6 C++ paper clarifies this.