Here's some contrived example code:
template<typename T> void Do(T arg) { (void)arg->b; }
namespace A {
struct Foo { int a; };
}
namespace B {
struct Foo { int b; };
struct Bar : A::Foo {
void Blah() { Do((Foo *)0); }
};
}
Which when compiled with gcc 4.8.2 (clang gives a similar error):
namespacebug.cpp: In instantiation of ‘void Do(T) [with T = A::Foo*]’:
namespacebug.cpp:10:34: required from here
namespacebug.cpp:1:39: error: ‘struct A::Foo’ has no member named ‘b’
template<typename T> void Do(T arg) { (void)arg->b; }
^
Note in the error it refers to T = A::Foo even though at the call-site I am creating a Foo within namespace B. If I remove the base class decl (: A::Foo) then all compiles fine.
This appears to suggest that inheriting from A::Foo somehow brings it into my namespace and matches it to my use of Foo? What C++ "feature" causes this?
(Of course, this issue can easily be fixed by namespacing my use of Foo, but that's not the question.)
Because of the injected-class-name rule, the name of a class is visible as though it were a member.
9/2
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.
So it is as though class A::Foo contains a member Foo which names the type A::Foo. Since name lookup in Bar::Blah() considers base members of Bar before namespace members, the name lookup for Foo finds the injected-class-name, which names A::Foo.
Does inheriting from a class bring it into the namespace?
Sort of. If lookup of a name fails in the class, then lookup is continued in the base class.
10.2 Member name lookup
...
5 Otherwise (i.e., C does not contain a declaration of f or the resulting declaration set is empty), S(f,C) is initially empty. If C has base classes, calculate the lookup set for f in each direct base class subobject Bi, and merge each such lookup set S(f,Bi) in turn into S(f,C).
Member name lookup includes lookup of nested types too. Later in that same section, we find:
9 [ Note: A static member, a nested type or an enumerator defined in a base class T can unambiguously be found even if an object has more than one base class subobject of type T. Two base class subobjects share the non-static member subobjects of their common virtual base classes. —end note ]
Related
Consider a class D inherited from two classes B and C, each of which inherits not-virtually class A. There is a method f in A, and the same named method in B hiding the method from A. I would like to call A::f() from B-base class of D object as follows:
struct A { void f() {} };
struct B : A { void f() {} };
struct C : A {};
struct D : B, C {};
int main() { D{}.B::A::f(); }
Unfortunately, it works only in MSVC, while both GCC and Clang produce the error:
error: 'A' is an ambiguous base of 'D'
demo: https://gcc.godbolt.org/z/jY3v876hK
It looks like GCC/Clang accept but completely ignore B:: prefix in B::A::f(). Are they right in doing so according to the standard?
Are they right in doing so according to the standard?
Yes. The nested name specifier in A::, B::A, or D::B::A all serve the same purpose, to name the class A.
[basic.lookup.classref]
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 ([class.member.lookup]) and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.
The nested name specifier does not name "a path" to the member, it names a base of D. And to name A results in ambiguity.
Other answers show why the compiler is correct.
In order to achieve what you want, you can do:
int main() { static_cast<B&&>(D{}).A::f(); }
It's a bit ugly, though.
https://gcc.godbolt.org/z/Mzs8rsdf5
Consider a class D inherited from two classes B and C, each of which inherits not-virtually class A. There is a method f in A, and the same named method in B hiding the method from A. I would like to call A::f() from B-base class of D object as follows:
struct A { void f() {} };
struct B : A { void f() {} };
struct C : A {};
struct D : B, C {};
int main() { D{}.B::A::f(); }
Unfortunately, it works only in MSVC, while both GCC and Clang produce the error:
error: 'A' is an ambiguous base of 'D'
demo: https://gcc.godbolt.org/z/jY3v876hK
It looks like GCC/Clang accept but completely ignore B:: prefix in B::A::f(). Are they right in doing so according to the standard?
Are they right in doing so according to the standard?
Yes. The nested name specifier in A::, B::A, or D::B::A all serve the same purpose, to name the class A.
[basic.lookup.classref]
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 ([class.member.lookup]) and the name, if found, is used. Otherwise it is looked up in the context of the entire postfix-expression.
The nested name specifier does not name "a path" to the member, it names a base of D. And to name A results in ambiguity.
Other answers show why the compiler is correct.
In order to achieve what you want, you can do:
int main() { static_cast<B&&>(D{}).A::f(); }
It's a bit ugly, though.
https://gcc.godbolt.org/z/Mzs8rsdf5
Consider the following snippet:
struct Base{
void f() {}
};
struct Derived: public Base{
void f() { Base::f(); }
};
Derived d;
int main(){
d.f();
}
The snippet works fine. i.e., d.f() calls Base::f(). But I can't find a quote from the standard supporting this.
All standard references below refer to N4659: March 2017 post-Kona working draft/C++17 DIS.
This is [class.qual]/1 [extract, emphasis mine]:
If the nested-name-specifier of a qualified-id nominates a class, the name specified after the nested-name-specifier is looked up in the scope of the class [...]
In your example, Base:: is the nested-name-specifier, and the name specified after it is f(), for which lookup is thus performed in the scope of the class Base. This rule will not be affected by shadowing on the call site, as we are using a qualified name, and we may note that if we remove f() from Base the qualified id will (as expected for qualified lookup) not search for f() of the scope of the derived class.
struct Base {};
struct Derived: public Base {
void f() {
Base::f(); // error: 'f' is not a member of 'Base
}
};
We may also note that [class.qual]/3 particularly mentions this case:
A class member name hidden by a name in a nested declarative region or by the name of a derived class member can still be found if qualified by the name of its class followed by the :: operator.
but as pointed out in a comment by #LanguageLawyer, this paragraph should arguably be non-normative(1).
(1) [class.qual]/3 gives a specific example of how [class.qual]/1 can apply / the effect of it. However, these kind of segments in the standard are typically non-normative (examples, notes, and so on), whereas [class.qual]/3, which brings no ”new normative" value (already covered in [class.qual]/1), is not wrapped as a note or an example.
This code example would describe the language feature I find non-intuitive.
class A {
public:
A() {}
};
class B: private A
{
public:
B() {}
};
class C: public B
{
public:
C() {}
void ProcessA(A* a) {
}
};
int main() {
C c;
}
Compilation of this code with Apple LLVM version 4.2 on Mac produces an
test.cc:16: error: ‘class A’ is inaccessible
test.cc:16: error: within this context
Replacing void ProcessA(A* a) with void ProcessA(::A* a) would make it build but I don't understand why should I use absolute class name here.
Is it a language feature that is there to avoid certain kind of errors or is it just a dark C++ grammar corner like requirement to put space between angle brackets (> >) in templates parametrized with other templates.
Thanks!
Human talk
I 'll start by describing what happens here -- forgive me if you already know this, but it creates necessary context for the follow-up.
The compiler resolves the unqualified A to ::C::A (the result will be the same if you make the change at source level yourself). Since ::C::A is inaccessible an error message is emitted.
You are proposing that the compiler should detect that ::C::A is inaccessible and the reference to A should then be considered a reference to ::A as a fallback. However, ::C::A and ::A may easily be two entirely different things.
Automatically guessing what should be done here is not only prone to introducing bugs and/or hair-pulling¹, but also completely contrary to the spirit of C++.
Standardese
Confirmation that this behavior is conformant and by-design, directly from the C++11 standard.
§9/2 says:
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.
This means that inside the scope of class C, A is an injected-class-name.
§3.4/3 states that the injected-class-name is a candidate for name lookups:
The injected-class-name of a class is also considered to be a member
of that class for the purposes of name hiding and lookup.
§3.4/1 clarifies that the inaccessibility of the base A does not prevent the injected-class-name A from being considered:
The access rules are considered only once name lookup and function
overload resolution (if applicable) have succeeded.
§11.1/5 gives a direct explanation of the exact situation under discussion:
[Note: In a derived class, the lookup of a base class name will find
the injected-class-name instead of the name of the base class in the
scope in which it was declared. The injected-class-name might be less
accessible than the name of the base class in the scope in which it
was declared. —end note ]
The standard also gives this example, which is equivalent to yours:
class A { };
class B : private A { };
class C : public B {
A *p; // error: injected-class-name A is inaccessible
::A *q; // OK
};
¹ Imagine what happens if A is initially a public base, then later becomes private during a refactoring. Also imagine that ::A and ::C::A are unrelated. You would expect that a call like a->foo() (which used to work) would fail because foo is no longer accessible, but instead of this the type of a has changed behind your back and you now get a "there is no method foo" error. Huh?!? And that's of course far from the worst that could happen.
Is the following code valid with C++ standard? It's strange that C::B would work because struct B is in A's namespace. But it does compile fine with gcc.
struct A { struct B {}; };
struct C : public A::B {};
struct D : public C::B {};
If this is standard conforming C++, what are reasonable applications for this construct?
Thanks.
Yes, it's valid C++. A class in its own scope (so both B and B::B refer to the same class B), and a class's parent class is in its own scope. So since B is in C's scope and B is in its own scope, C::B refers to B which is A::B.
(Side note: do not confuse a namespace with a scope.)
C++03 §9 paragraph 2 says:
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.
This is standard conforming C++.
A reasonable application of nested classes (through scope resolution) is the pImpl design pattern (though you can implement it in a way that will not demonstrate nested classes, but here I choose to demonstrate nested classes).
Note: Inheritance is one of the facets that can be confusing but the real concept is the scope resolution of a nested class.
//ExposedClass in Exposed.h
class CExposedClass
{
public:
CExposedClass();
~CExposedClass();
void doThis();
void doThat();
private:
class CXImpl;
CXImpl *pImpl;
};
//ExposedClass Impl in Exposed.cpp
#include "Exposed.h"
class CExposedClass::CXImpl
{
int someData;
};
CExposedClass::CExposedClass():pImpl(new CXImpl()){}
CExposedClass::~CExposedClass(){delete pImpl;}
void CExposedClass::doThis(){}
void CExposedClass::doThat(){}
class defined within the scope of one class is addressed from another scope using scope resolution.