-4

I just read (for the k'th time)

C++ static virtual members?

Which is a question about simulating virtual static members. My question is - what made the C++ standards committe (or Bjarne Stroustrup before that) not add this feature to C? Are they known to break anything? Or impede the performance of anything (even when not used)?

To better illustrate what I'm taking about over the feature definition itself, here is some code:

// This does not compile!
class Base {
    // A pure virtual member - perhaps need to indicate that somehow
    virtual static const string ToWhom; 
    void sayHello() {
        cout << "Hello, " << ToWhom << "!" << endl;
    }
};
class World : public Base {
    static virtual const string ToWhom = "the entire world"s; // C++14 literal
};
class Everybody : public Base {
    static virtual const string ToWhom = "everybody around"s;
};

Note: I'm not asking about your opinion or whether adding these is a good idea, I'm asking about the history and the official considerations.

Bhargav Rao
  • 50,140
  • 28
  • 121
  • 140
einpoklum
  • 118,144
  • 57
  • 340
  • 684

5 Answers5

8

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.

Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
  • Your choice of using some example code motivated me to do the same. You could adapt your answer to reflect my choice of a use-case (which I think is a bit simpler, or at least more terse) but it's fine this way too I guess. – einpoklum Apr 25 '15 at 07:04
  • 1
    @einpoklum Your example went further and introduced a static virtual data member. Virtual data members, even the non-static ones, are missing from the language altogether. – Sergey Kalinichenko Apr 25 '15 at 09:33
  • I know they're missing, the question is about *how come* they're missing... – einpoklum Apr 25 '15 at 10:27
  • @einpoklum Virtual data members are missing because the difficulties with their implementation would dwarf the potential benefits, considering that you can easily imitate them with virtual functions with only a syntactic difference. – Sergey Kalinichenko Apr 25 '15 at 10:42
4

A virtual method would require a virtual table, a virtual table would require an instance with a vtable pointer, static member methods are not called through an instance, therefore it is simply not possible.

From the "problem" described in you question, it would appear that you'd expect polymorphic behavior from usage in this format:

Everybody::sayHello();
World::sayHello(); 

But that doesn't really mandate polymorphism, because you point the kind of functionality you want invoked - it is clear that Everybody::sayHello() invokes the functionality for Everybody. There is no "polymorphic ambiguity" - there is no unknown type whose functionality needs to be looked up to produce the expected polymorphic behavior.

Therefore you don't really need dynamic dispatch to solve this problem, you can simply use shadowing - even though you cannot overload static methods as virtual methods, you can still overload by shadowing them, and it is OK, because you specify the type therefore you will get the correct version.

You can either shadow the static methods manually:

struct Base {
  static string toWhom() { return ""; }
  static void sayHi() { cout << "Hello " + toWhom(); }
};

struct World : Base {
  static string toWhom() { return "World"; }
  static void sayHi() { cout << "Hello " + toWhom(); }
};

struct Everyone : Base {
  static string toWhom() { return "Everyone"; }
  static void sayHi() { cout << "Hello " + toWhom(); }
};

Or use a class template to have it done for you, so you only have to shadow the "virtual static method", the template will make sure the correct type to invoke the static method for:

template <typename T>
struct Base {
  static string toWhom() { return ""; }
  static void sayHi() { cout << "Hello " + T::toWhom(); }
};

struct World : Base<World> {
  static string toWhom() { return "World"; }
};

struct Everyone : Base<Everyone> {
  static string toWhom() { return "Everyone"; }
};

Then

Everybody::sayHello();
World::sayHello(); 

both solutions will produce the expected result. There is simply no need for any polymorphism to accomplish this goal. Note that it would certainly be possible to implement exactly what you want, but that would just give you the possibility to create a less efficient solution - because polymorphism has both memory and CPU time overhead, and C++ is a language whose primary concern is performance and efficiency. Therefore it doesn't support a feature that is unneeded, since it is already possible to do what you ask for, and it will be blazing fast, because such simple functions will not even be called - they will be inlined. There is a tremendous performance difference between a function that is inlined and the invoking of a virtual method (like 20x for such trivial functions), and adding another level of indirection for the sake of implementing static virtual members will only make it worse.

I hope now I've given compelling answers to why is this not possible in C++, why it is not needed, and why it makes no sense making it possible in that particular language whatsoever. You basically want to use polymorphism in a scenario that doesn't call for it, for the sake of making the language "easier" - well, you can't have it both ways, C++ is hard because it is fast, just like easier languages are all slower than C++.

Lastly - if you feel like this is such an important language feature to have - you can always request the feature from the standard committee ;)

dtech
  • 47,916
  • 17
  • 112
  • 190
  • Your facts are true, but your conclusion is invalid... you would just have to have some kind of vtable for statics whenever you make the call. When a vtable is not available, it is sort of meaningless to do anything other than call a specific implementation. – einpoklum Apr 25 '15 at 06:58
  • @einpoklum - care to elaborate a bit? Because the way I see it, in order for dynamic dispatch to be used, you have to specify the vtable somehow. If you call `BaseClass::someVirtualStaticFoo()` then you will be invoking from the hypothetical static vtable of `BaseClass` - you cannot implement polymorphism this way. Now if it was possible to call static methods from instances, then I guess that could work, but then again, those would pretty much be still non-static member functions because the will still need the `this` pointer in order to access the vtable. – dtech Apr 25 '15 at 10:49
  • (Assuming we're not talking about some fancy reflection mechanism in which `Class` inherits from `Class`) there are two ways you call a static method of MyBaseClass: from/via an instance of the class (or an instance of a derived class), or from 'outside' without using any instance. In the latter case, it's meaningless to talk about virtual-anything; in the former case, the vtable of an instance could point to the statics vtable (or have a copy of it, depending on implementation); and in invocation would use that vtable. Am I making more sense now? – einpoklum Apr 25 '15 at 11:01
  • @einpoklum - I didn't say it is not possible to come up with some mechanism to do it, only that it is not possible to do in C++, because you can't invoke static functions from instances. Also that would kind of defeat the purpose, because you wouldn't be able to call such a method without an instance, which is one of the advantages of static methods. In the case of having a vtable for the particular subclass it is pointless, because you will always be invoking the method for that particular class anyway, only with the perf penalty of dynamic dispatch. – dtech Apr 25 '15 at 11:11
  • Usually, there are good reasons for what the language can and cannot do. If you want to use virtual methods, they have to be member methods, you gain nothing from wasting even more memory or CPU time on directions. You can just pretend it is static and simply not address any instance locals and there you have it. Any implementation of static virtuals will only add overhead without any clear advantage whatsoever. Therefore it is pointless to do in a language that is all about performance and efficiency. – dtech Apr 25 '15 at 11:13
  • I described a pretty straightforward use case in the question: data (and perhaps logic) which is derived-class-specific but not instance-specific. I'm not saying I can't live without it, but it looks pretty innocuous, and doesn't seem to break the "don't pay for abstractions you don't use" principle. Also it would obviate the `__CLASS__` macro :-) – einpoklum Apr 25 '15 at 11:14
  • Wouldn't this work only for direct-descendants? What if, on top of your example, I want to have `class CruelWorld: public World { static string toWhom() { return "cruel world"; } }`? Perhaps your solution would be more robust as a sort of a mix-in, using multiple inheritance? – einpoklum Apr 25 '15 at 12:05
  • To be fair, I've been programming quite a lot the past few years, and I mean practical programming, not pointless little proofs of concepts, and I've never had such a "problem" - it is basically an anti pattern. You are trying to do something only because it can't be done, which is a bad idea, you don't do it because you have to in practice. This is not what static methods are used for, and this is not how you go about doing things in C++. The problem is not that the fork has been designed bad for eating soup, the problem is you are trying to eat soup with a fork :) – dtech Apr 25 '15 at 12:14
  • So, your answer is that in real-life-programming, the need for virtual static members doesn't come up? That's a valid answer I suppose... but, again, it's more virtual static data members that my question focuses on. – einpoklum Apr 25 '15 at 12:33
  • There are no virtual data members in C++ whatsoever, much less static. I agree there might be use for them, but the C++ STD committee has not deemed them necessary. There are other ways to solve those problems in C++ more efficiently. May not be the prettiest way, but it will surely be the most performance efficient. C++ is the least elegant language I know of :) – dtech Apr 25 '15 at 12:42
  • "the C++ STD committee has not deemed them necessary" - that's exactly what I'm asking about. Why? When? With what arguments? – einpoklum Apr 25 '15 at 12:43
  • It is a standard committee - logic and reason are not always first priority. There is a lot of corporate politics involved, lot of stubbornness and pride, it is my personal opinion they are deliberately making C++ a nightmare to work with to keep it from wide adoption and becoming "mainstream" and lowly paid. The right attitude is either accept their decisions or don't use C++ - there is no point asking for their reasons or protesting. – dtech Apr 25 '15 at 12:49
  • I wasn't protesting, I though maybe there was a significant technical consideration against this feature which I was missing... – einpoklum Apr 25 '15 at 12:51
  • I've told you the reason - whatever the problem that mandates it, there is already another way to solve it, it will be uglier and more verbose, but also more efficient. Efficiency is top priority in C++, thus the heavy development of templates and meta programming in the language. Some languages don't even have non-virtual functions, there is unavoidable overhead, so they decide why not add the extra functionality while we are paying... – dtech Apr 25 '15 at 12:55
  • Override, not overload. – Oktalist Apr 25 '15 at 22:01
1

You can implement polymorphic behaviour statically using the CRTP. For example,

#include <iostream>
using namespace std;

template <typename Derived>
struct Base {
    static void sayHello() {
        cout << "Hello, " << Derived::toWhom() << "!" << endl;
    }
};

struct World : public Base<World> {
    static string toWhom() {
        return "world";
    }
};

struct Everybody : public Base<Everybody> {
    static string toWhom() {
        return "everybody";
    }
};

int main() {
    World::sayHello();
    Everybody::sayHello();
    return 0;
}

There are lots of detailed questions and answers on SO about CRTP if you want more information on the topic.

Community
  • 1
  • 1
wakjah
  • 4,541
  • 1
  • 18
  • 23
  • So, you're saying the fact of this behavior being emulatable using static methods is the reason for not bothering to support it directly? – einpoklum Apr 25 '15 at 07:00
  • Also, same question as for ddriver: What if I now want to have `class CruelWorld: public World { static string toWhom() { return "cruel world"; } }` – einpoklum Apr 25 '15 at 12:06
0

A static member function does not act on any particular object.

A virtual member function is the means of specifying that behaviour (the version of the function called) depends on the type of object being acted on (e.g. the code executed by object->foo() depends on the actual type of object).

That is how the two concepts of virtual and static are specified in C++ (at least, in this context). The two concepts are mutually exclusive.

It is not possible to have runtime behaviour that doesn't act on any object depend on the type of object it acts on. It is a logical fallacy.

Peter
  • 35,646
  • 4
  • 32
  • 74
  • Subclasses of some class may have data that is instance-specific and data that is subclass-specific, with methods of the base class utilizing both. You're assuming such members would be used irrespective of any particular object, but as the examples in the answers here and my edit show, this is not the relevant use-case for virtual static members. – einpoklum Apr 25 '15 at 07:03
  • I'm assuming no such thing. I am describing the way these things are specified in C++. You're the one who is describing a use case that is not supported by C++, and asking why. – Peter Apr 25 '15 at 07:30
  • Well, I am asking about a feature which the language does not have, which would be used in a way which is not possible in C++ right now... – einpoklum Apr 25 '15 at 07:43
  • Sure. But I answered the question you originally asked. I was not offering to debate whether the feature should be part of C++. What you describe is a use case with relatively little applicability, so isn't really worth the changes (e.g. to the C++ object model) that would be needed. – Peter Apr 25 '15 at 08:01
0

Since you are comparing with Objective-C: In Objective-C, classes are themselves objects. In C++, classes are a compile-time construct; they don't exist at runtime. C++ static class methods are just plain old extern functions with some interesting syntax. Because there are no class objects unlike in Objective-C, virtual static functions don't make any sense.

gnasher729
  • 51,477
  • 5
  • 75
  • 98
  • Umm, I actually don't know Objective-C at all, but: 1. They do make some sense, see [wakjah's answer](http://stackoverflow.com/a/29859293/1593077) 2. The use case in my question is virtual static data members. 3. I'm asking about a potential feature which could be added to the language, not about its current semantics. – einpoklum Apr 25 '15 at 12:36