MikeT
MikeT

Reputation: 1644

Is this valid C++ code?

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?

Upvotes: 23

Views: 1022

Answers (4)

Alexandre Bell
Alexandre Bell

Reputation: 3241

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.

Upvotes: 25

Richard Corden
Richard Corden

Reputation: 21721

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>'
  }
};

Upvotes: 11

sbi
sbi

Reputation: 224029

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.

Upvotes: 1

jkeys
jkeys

Reputation: 3955

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.

Upvotes: 0

Related Questions