17

I hope the title actually describes what I wanted to ask...

I wrote a piece of code that compiles with gcc and works as I intended. However, it does not compile with llvm and the code executes differently when compiled with icc!
Here is an example of the problem:

#include <iostream>

using std::cout; using std::endl;

class A {
public:
  virtual void foo() { cout << "A::foo()" << endl; }
};

class B : public A {
public:
  typedef A  base;
  virtual void foo() { cout << "B::foo()" << endl; }
};

int main() {
  typedef B  base;
  base* bp = new B();
  bp->base::foo(); 
}

gcc output: A::foo()
icc output: B::foo()

Could somebody explain what does the standard say about this case?

Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
Nowakus
  • 571
  • 4
  • 7
  • 5
    I would say it's a bug in GCC and ICC, as `B::base` is not a _member_ of `B`, which means that it shouldn't be possible to access it as a member (`bp->base`). – Some programmer dude Jun 20 '12 at 08:26
  • 1
    I agree with @JoachimPileborg, moreover `base` could be interpreted as B in this scope. Have you compiled with Warnings flags? (-Wall for gcc) – Jaffa Jun 20 '12 at 08:28
  • 1
    Isn't it just undefined behaviour, because `main` is not of the required form? Looks like all compilers are right. – Kerrek SB Jun 20 '12 at 08:34
  • @Nawaz: Are you sure ? I never could wrap my head in the name lookup rules; Clang considers that the two `base` are ambiguous and does not prefer one over the other. I really wish we would have an authoritative answer here, 3 compilers - 3 behaviors... it's a weird piece for sure. – Matthieu M. Jun 20 '12 at 08:35
  • @KerrekSB: Okay, it should now be ready to use... and still you'll get the issue. – Matthieu M. Jun 20 '12 at 08:37
  • You could post a comment on [STL's series](http://channel9.msdn.com/Series/C9-Lectures-Stephan-T-Lavavej-Core-C-/Stephan-T-Lavavej-Core-C-1-of-n), which is all about name lookup. – Kerrek SB Jun 20 '12 at 08:37
  • @MatthieuM.: OK, *now* it's a bug :-) – Kerrek SB Jun 20 '12 at 08:38
  • What gcc version are you using? The code you pasted fails to compile in all versions of gcc I have available (4.3.4 through 4.7.0). Did you maybe compile some other code, and just showing a praphrasation of it here? – PlasmaHH Jun 20 '12 at 08:38
  • @PlasmaHH: Really? The code seems to compile and run on ideone, which uses gcc 4.3.4: http://ideone.com/daJbT. It also works if you enable c++11, using gcc 4.5.1 : http://ideone.com/KT1Jj. – Luc Touraille Jun 20 '12 at 08:41
  • @LucTouraille: If you compare the post edits with the timestamp of my comment, taking into account that it takes a while to write it, you would notice that at the time of writing, there was code that did not compile. – PlasmaHH Jun 20 '12 at 08:44
  • @PlasmaHH: Ok, I thought your comment was in response to Matthieu's edit, sorry about the confusion. – Luc Touraille Jun 20 '12 at 09:06
  • So... it seems that there is a bug in both Clang and icc and that gcc got it right. Anyone has a fresh copy of Clang to test on the trunk ? – Matthieu M. Jun 20 '12 at 09:09

2 Answers2

7

From C++11, §3.4.5/4:

If the id-expression in a class member access is a qualified-id of the form
    class-name-or-namespace-name::...
the class-name-or-namespace-name following the . or -> operator is first looked up in the class of the object expression and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.

I don't think it can be clearer. This finds B::base, so the output should be A::foo().

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
James Kanze
  • 150,581
  • 18
  • 184
  • 329
  • Thank you for help, you guys are awesome. This particular paragraph is indeed very clear and exactly to the point! – Nowakus Jun 20 '12 at 14:25
6

I think this part of the standard is relevant:

3.4.3.1 Class members [class.qual]

1) If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-namespecifier is looked up in the scope of the class (10.2), except for the cases listed below. The name shall represent one or more members of that class or of one of its base classes (Clause 10). [ Note: A class member can be referred to using a qualified-id at any point in its potential scope (3.3.7). —end note ] The exceptions to the name lookup rule above are the following:

— a destructor name is looked up as specified in 3.4.3;

— a conversion-type-id of a conversion-function-id is looked up in the same manner as a conversion-type-id in a class member access (see 3.4.5);

— the names in a template-argument of a template-id are looked up in the context in which the entire postfix-expression occurs.

— the lookup for a name specified in a using-declaration (7.3.3) also finds class or enumeration names hidden within the same scope (3.3.10).

base:: in this case seems to "nominate" a class, so the look up is done in scope of the class. I don't see how any of the exception cases could apply, so it is the scope of the class, as such base is equivalent to A.

(5.1.1-8 indicates that it is a qualified-id in that case and that 3.4.3.1 applies)

PlasmaHH
  • 15,673
  • 5
  • 44
  • 57
  • Agreed. It seems quite clear to me as well. The code is correct, well defined, and the output should be `A::foo`. – James Kanze Jun 20 '12 at 08:58