views:

710

answers:

4

I had the following code, which was basically,

class foo {
  public:
    void method();
};

void foo::foo::method() { }

I had accidentally added an extra foo:: in front of the definition of foo::method. This code compiled without warning using g++(ver 4.2.3), but errored out using Visual Studio 2005. I didn't have a namespace named foo.

Which compiler is correct?

A: 

Is there a namespace foo in some other module that you include (and you were just ignorant of it)? Otherwise, it is not correct. I am not sure why g++ allowed this.

Hooked
Actually, g++ allows it. I just tested.
Matt H
"I am not sure why g++ allowed this."
Hooked
+22  A: 

If I read the standard correctly, g++ is right and VS is wrong.

ISO-IEC 14882-2003(E), §9.2 Classes (pag.153): A class-name is inserted into the scope in which it is declared immediately after the class-name is seen. The class-name is also inserted into the scope of the class itself; this is known as the injected-class-name. For purposes of access checking, the injected-class-name is treated as if it were a public member name.

Following on the comments below, it's also particularly useful to retain the following concerning the actual Name Lookup rules:

ISO-IEC 14882-2003(E), §3.4-3 Name Lookup (pag.29): The injected-class-name of a class (clause 9) is also considered to be a member of that class for the purposes of name hiding and lookup.

It would be odd if it wasn't, given the final part of text at 9.2. But as litb commented this reassures us that indeed g++ is making a correct interpretation of the standard. No questions are left.

Krugar
Interesting, does this explain the fact that foo::foo::foo::method also works?
MikeT
Not entirely, no. The description for that I believe can be found 3.4.3.1 - Qualified Name Lookup:The name of a class or namespace member can be referred to after the :: scope resolution operator (5.1)applied to a nested-name-specifier that nominates its class or namespace. During the lookup for a namepreceding the :: scope resolution operator, object, function, and enumerator names are ignored. If thename found is not a class-name (clause 9) or namespace-name (7.3.1), the program is ill-formed
Krugar
Of especial note, "During the lookup for a name preceding the :: scope resolution operator, object, function, and enumerator names are ignored."
Krugar
Another quote: *"The injected-class-name of a class (clause 9) is also considered to be a member of that class for the purposes of name hiding and lookup."* from `3.4/3`. Lookup in classes is defined in terms of member names - so it takes this quote to actually make it valid for cases like in the question. Note that comeau is wrong with `struct A { void f() { A::A a; } };` rejecting that valid code :) If you include that quote in your answer, i will +1 you xD
Johannes Schaub - litb
Certainly litb. But no need for the +1. I find 9.2 self-explanatory in many regards. But I do agree 3.4/3 makes it a lot more evident. A moment while I look it up on my copy of the standards...
Krugar
BTW, Comeau is right that it rejects "A::A a;" in my code above. That's because of 3.4.3.1/1a, which says that "A::A" in that code refers to the constructor, and not to the class. This issue is quite tricky...
Johannes Schaub - litb
+1  A: 

Comeau online accepts it without any hickups, so it's either valid or the second bug in como I have found in almost ten years.

sbi
+9  A: 

Krugar has the correct answer here. The name that is is being found each time is the injected class name.

The following is an example which shows at least one reason why the compiler adds the injected class name:

namespace NS
{
  class B
  {
  // injected name B    // #1
  public:
    void foo ();
  };

  int i;                // #2
}

class B                 // #3
{
public:
  void foo ();
};


int i;                  // #4

class A :: NS::B
{
public:
  void bar ()
  {
    ++i;           // Lookup for 'i' searches scope of
                   // 'A', then in base 'NS::B' and
                   // finally in '::'.  Finds #4

    B & b = *this; // Lookup for 'B' searches scope of 'A'
                   // then in base 'NS::B' and finds #1
                   // the injected name 'B'.

  }
};

Without the injected name the current lookup rules would eventually reach the enclosing scope of 'A' and would find '::B' and not 'NS::B'. We would therefore need to use "NS::B" everywhere in A when we wanted to refer to the base class.

Another place that injected names get used are with templates, where inside the class template, the injected name provides a mapping between the template name and the type:

template <typename T>
class A
{
// First injected name 'A<T>'
// Additional injected name 'A' maps to 'A<T>'

public:
  void foo ()
  {
    // '::A' here is the template name
    // 'A' is the type 'A<T>'
    // 'A<T>' is also the type 'A<T>'
  }
};
Richard Corden