Dot or arrow operator vs. scope resolution operator for accessing base subobject
c++, dot-operator, inheritance, namespaces, scope-resolution
Solution
- No, you can have a member named `Base` which is separate from the `Base` subobject. The `::` punctuator limits name resolution to ignore a member object name.
- See #1. Usually the answer is no because you would be crazy to have a member alias a base on purpose. But, it can happen with templates where a class may be unaware of the names of its bases.
- No. Member subobjects and base subobjects are both subobjects, but they are accessed differently.
- It always refers to the class, and the name `Base` itself is inherited from `Base`. If you have a crazy member alias, then you need to use some other reference to `Base` such as a namespace-qualified id.
- `static_cast< Base & >( derived_obj )`.
- No, `::` has higher precedence than `.` so the `Base::foo` member is looked up inside `myderived` and then the function call operator is applied. However parens are not allowed around `(Base::foo)` because `::` is not an operator generating a subexpression; this is why I prefer to call it a punctuator.
- See #6. `myderived.Base` is not anything by itself because Base groups with `::` before `.`.
- Right. Note that classes aren't namespaces; they are different things which both happen to work with the same scope notation.
- This seems to be explaining C++ in terms that might apply to another language. In Java for example a class is sort-of an object with own data members. In C++ `static` class members and namespace members are completely separate objects which may be defined anywhere.
- `Base::Base::Base::` works because a class' name is injected into itself as if it were a member `typedef`. VC is probably making an error and interpreting that as a reference to the constructor. Per the spec, the special typedef (called an injected-class-name) refers to the constructor under special circumstances, but before the scope operator is not such a case.
Every class contains an implicit `typedef` to itself. Again, namespaces and classes are completely different things.
The prefix `::` is not itself the name of the global namespace but just a special case in the grammar to compensate for its lack of a name. Likewise, for better or worse, you cannot declare
namespace global = :: ; // error: :: alone does not name anything.
Problem
C++ Given a base class `Base` and a derived class `Derived`, the first thing constructed by `Derived`’s constructor is the `Base` subobject. Since it’s called a subobject, I assumed it can be accessed from client code like any other member object by using the dot operator on the `Derived` object. I also assumed it can be accessed from `Derived`’s implementation code by `this->Base`. A statement comprised entirely of the name of an object that has already been initialized followed by a semicolon should compile but also have no effect. Following that logic, given a `Derived` object `myderived`, I tried: `myderived.Base;` in client code and `this->Base;` in `Derived`’s implementation and neither statement compiles. Why? I know `Base`, by itself, is the name of the `Base` class and not of a `Base` object. But I thought `Base` qualified by the `myderived.` (client code) or `this->` (implementation code) prefix refers to the base subobject because `Base`, without any prefix qualifications, is the way the `Base` subobject is referred to in `Derived`’s constructor initializer. Refer to the below code, which (commented-out code aside) works in VC12 and g++ 4.8. `Derived` extends `Base` and `Derived`’s definition declares a `Base` data member `membase`, so my `Derived` object should contain two `Base` objects. Assuming the successful compilation isn’t the result of any compiler-Standard-nonconformity, the console output (in the comments), which shows different values for the `int` members `n` for the two different `Base` objects, implies that in `Derived`’s ctor initializer, `Base` refers to the inherited `Base` subobject whereas `membase` refers to the declared data member object. In `Derived`’s ctor initializer, `Base` refers specifically to the inherited subobject, not just any `Base` object nor the `Base` class. ``` #include <iostream> struct Base { Base(int par) : n(par) {} void foo() { std::cout << "Base: " << n << std::endl; } int n; }; struct Derived : Base { Derived() : Base(2), membase(3) {} Base membase; void foo() { std::cout << "Derived: " << n << std::endl; } // void g() { this->Base; } // Error in VC12 & g++ 4.8 // ^ VC12: error C2273: 'function-style cast' : illegal as // right side of '->' operator }; int main() { Derived myderived; // myderived.Base; //Error in VC12 & g++ 4.8 // ^ VC12: error C2274: 'function-style cast' : illegal as // right side of '.' operator myderived.foo(); // OUTPUT: "Derived: 2" myderived.Base::foo(); // OUTPUT: "Base: 2" myderived.membase.foo(); // OUTPUT: "Base: 3" } ``` Again, shouldn’t `myderived.Base;` or `this->Base;` uniquely refer to the inherited `Base` subobject and compile? Does the `Base` in `myderived.Base` or `this->Base` refer to the `Base` subobject or the `Base` class or anything at all? In general, are inherited base subobjects considered data members of derived classes? From the perspective of `Derived`, does `Base` only refer to the inherited subobject within the context of `Derived`’s constructor initializer and only refer to the `Base` class outside `Derived`’s ctor initializer? How can I access the inherited `Base` subobject through the `Derived` object, as in, how can I express “the inherited `Base` subobject of the `Derived` object” in `Derived`’s implementation code and in client code? The use of the scope resolution operator in `myderived.Base::foo()`, where `foo()` is a method of `Base`, compiles in VC12 and g++ 4.8. Does this mean `Base` is a data member of `myderived`, since it is qualified by `myderived` and the dot operator? If so, then is that `Base` the `Base` class or the `Base` subobject? But `myderived.Base.foo()` doesn’t compile. AFAIK access of an object’s member is qualified in client code by the object name and dot operator. Two kinds of things that are qualified by the scope resolution operator, instead of the object name and dot operator, are (a) outside access to anything that belongs to a namespace and (b) the names of static data members and names of member functions of definitions defined outside their class definition, in which case the `Base` that precedes the `::` refers to the `Base` class, not any `Base` instance. Does this mean the `Base` in `myderived.Base` is a namespace or refers to the class? If so, then is its being a namespace or referring to the class conditional upon whether it is appended by a `::` followed by a member of `Base`? If the answer to #7 is yes, then why? It would seem incongruous with the following logic: a namespace’s enclosure of one variable does not in itself enable the namespace to enclose or construct other instances of the variable’s type. The namespace only owns one instance of that type--the variable it encloses. The same goes for a member that is part of a class, as in, static data member. The class only owns one instance of that type--the static data member it encloses. In contrast, there are as many same-named non-static data members of a class as there are instances of the class. Given method `h()` of `Base` and a `Derived` object `myderived`, `myderived.Base::h();` compiles in VC12 and g++ 4.8. Addionally, g++ 4.8 can take any arbitrary number of extra `Base::`s in that statement, like `myderived.Base::Base::h();`. Such a statement seems to imply `Base` is a member of `Base`. But VC12 gives `error C3083: '{ctor}': the symbol to the left of a '::' must be a type`. But given `Base` object `mybase`, VC12 compiles `mybase.Base::h();` just fine, which would also imply VC12 is fine with treating a class as a member of itself. But that contradicts its inability to compile the prior statement. Also, VC12 cannot compile any version of `mybase.Base::h();` that has any number of extra `Base::`s (e.g. `mybase.Base::Base::h()`), but g++ can. Which compiler is correct, if any? In any case, does that mean a namespace or class can contain itself? Given a global `int` variable `x`, the statement `::::x;` (with two scope resolution operators) doesn’t compile in either compiler, so I’m assuming the global scope doesn’t contain the global scope.