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.
Base
which is separate from the Base
subobject. The ::
punctuator limits name resolution to ignore a member object 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 )
.::
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.myderived.Base
is not anything by itself because Base groups with ::
before .
.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.