Why is a class allowed to have a static member of itself, but not a non-static member?
Because static
class members are not stored in the class instance, that's why a static
would work.
Storing an object inside another object of the same type would break the runtime - infinite size, right?
What would sizeof
return? The size of the object needs to be known by the compiler, but since it contains an object of the same type, it doesn't make sense.
Why have virtual static members not been added as a feature of C++?
First, let's look at an invalid example of how static virtuals could look:
// WARNING: This does not compile !!!
class Base {
static virtual string toWhom() {
return "unknown";
}
static void sayHello() {
cout << "Hello, " << toWhom() << "!" << endl;
}
};
class World : public Base {
static virtual string toWhom() {
return "world";
}
};
class Everybody : public Base {
static virtual string toWhom() {
return "everybody";
}
};
This would let you do this:
// WARNING: This does not work !!!
Everybody::sayHello(); // Prints "Hello, everybody!"
World::sayHello(); // Prints "Hello, world!"
The problem, however, is that a dispatch like this would not be easy to implement without changing the way static functions are called in C++.
Recall that non-static member functions get this
parameter implicitly. It is this
parameter that carries information about virtual functions with it. When you call a static function, however, nothing is passed that would identify the current class (i.e. Hello
vs. Everybody
in the example above). There are no implicit arguments, hence no access to virtual functions.
Going back to the example above, consider what would happen when Base::sayHello
calls toWhom()
. It needs to have some context to make a decision on which of the three function should be called - i.e. Base::toWhom
, World::toWhom
, or Everybody::toWhom
. Not only is this information missing, but there is also no existing mechanism in the language on which we could "piggyback" this functionality in a way similar to how a pointer to virtual functions is added to the data for the class.
Although it is true that this invocation mechanism could be changed, the authors of the language did not see compelling reasons for doing this.
A question about static members inside non-static classes and garbage collection
He doesn't know what he's talking about. Static members inside a non-static class do not prevent instances of the class from being garbage collected.
That said, statics can be on the stack or heap. It doesn't matter for garbage collection. What does matter is that the static parts of a type are not stored with instances of the type.
Is it allowed to call a non-static member function in a default member initializer?
As you found, this is legal, but brittle and not recommended. When you specify default initializers for class members those are just syntactic sugar for use this value in the class member initializer list. So, if we look at when we can call a member function we find [class.cdtor]/1 and [class.cdtor]/4 which states:
1) For an object with a non-trivial constructor, referring to any non-static member or base class of the object before the constructor begins execution results in undefined behavior. For an object with a non-trivial destructor, referring to any non-static member or base class of the object after the destructor finishes execution results in undefined behavior.
4) Member functions, including virtual functions ([class.virtual]), can be called during construction or destruction ([class.base.init]).[...]
emphasis mine
Since the constructor has begun executing, and we are allowed to call member functions, we are not in UB land.
The next thing we have to consider is construction order, since the members depend on that. That information is in [class.base.init]/13
Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
So, the members are constructed in the order they are declared in the class which means in your first example you refer to a
after it has been initialized so you are not in UB land.
In your second example you are referring to an object that has not yet been initialized and reading an uninitialized object's value is undefined behavior.
Why a lambda delegate declared as an instance field can only access static members?
Field initializers are static statements that are evaluated before the constructor is executed, which is why they can only access other static items.
You can see this if you try to replace the initializer with a method call; it will only compile if you make that method static.
Here is a runnable Demo fiddle: https://dotnetfiddle.net/IVtMHJ
Code from the fiddle:
using System;
public class Program
{
public static void Main()
{
Console.WriteLine("Demo is going to be created");
var d = new Demo();
Console.WriteLine("Demo has been created");
}
}
public class Demo
{
public Demo()
{
Console.WriteLine("Running constructor");
}
private IService _service;
Action<Guid> action = CreateAction();
public static Action<Guid> CreateAction() // Will get compile error above if we remove "static" here
{
Console.WriteLine("Running CreateAction");
return null;
}
}
public interface IService { }
Output:
Demo is going to be created
Running CreateAction
Running constructor
Demo has been created
If you move the CreateAction
call into the constructor then you can make it non-static:
public Demo()
{
Console.WriteLine("Running constructor");
action = CreateAction();
}
private IService _service;
Action<Guid> action;
public Action<Guid> CreateAction() // Now it does not need to be "static"
{
Console.WriteLine("Running CreateAction");
return null;
}
Output (from fiddle):
Demo is going to be created
Running constructor
Running CreateAction
Demo has been created
Why is a non-static data member reference not a variable?
Here's one way I can declare a variable in C++:
int scientist = 7;
After this declaration (and definition, in this case), I can use scientist
to read and set its value, take its address, etc. Here's another kind of declaration:-
class Cloud {
public:
static int cumulonimbus = -1;
};
This one is a bit more complicated, because I have to refer to the new variable as Cloud::cumulonimbus
, but I can still read and set its value, so it's still obviously a variable. Here's a yet different kind of declaration:-
class Chamber {
public:
int pot;
};
But after this declaration, there isn't a variable called pot
, or Chamber::pot
. In fact there's no new variable at all. I've declared a new class, and when I later declare an instance of that class it will have a member called pot
, but right now, nothing is called that.
A non-static data member of class doesn't create a new variable itself, it just helps you to define the properties of the class. If it did create a new variable, you'd be able to write code like this:
class Chamber {
public:
int pot;
};
void f(bool b) {
if (b)
Chamber::pot = 2;
}
What would that even mean? Would it find every instance of Chamber
and set all their pot
s to 2? It's a nonsense.
A quick footnote: the language of the standard here is talking specifically about references, but to make the examples easier, I've been using non-references. I hope you can see this doesn't change the principle of it.
Related Topics
Can a Variable Be Declared Both Static and Extern
Iterator Adapter to Iterate Just the Values in a Map
Auto' as a Template Argument Placeholder for a Function Parameter
How to Pass a Std::Function Object to a Function Taking a Function Pointer
Calling Base Class Definition of Virtual Member Function with Function Pointer
Using << Operator to Write to Both a File and Cout
Possible Memory Leak Without a Virtual Destructor
How Disastrous Is Integer Overflow in C++
How to Convert "Pointer to Pointer Type" to Const
Why Is a Class Allowed to Have a Static Member of Itself, But Not a Non-Static Member
Vector<Unique_Ptr<A> > Using Initialization List
How to Save Hicon to an .Ico File
Exception Error C0000005 in Vc++
C++ Regex for Overlapping Matches
How to Legally Reinterpret_Cast Between Layout-Compatible Standard-Layout Types