Why Is a Class Allowed to Have a Static Member of Itself, But Not a Non-Static Member

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 pots 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



Leave a reply



Submit