C++ Static Virtual Members

Can we have a static virtual functions? If not, then WHY?

No, because it doesn't make any sense in C++.

Virtual functions are invoked when you have a pointer/reference to an instance of a class. Static functions aren't tied to a particular instance, they're tied to a class. C++ doesn't have pointers-to-class, so there is no scenario in which you could invoke a static function virtually.

How to implement static virtual member function

One possible solution is CRTP with base class injection.

template <typename T, typename... Bases> 
struct CRTPFooInjector : Bases...
{
virtual void vfoo() { T::foo(); }
};

This is your injector template. It only implements the virtual version of foo, nothing else.

struct Base: CRTPFooInjector<Base>
{
static int foo() { std::cout << "Base::foo()" << std::endl; }
};


struct Der1 : CRTPFooInjector<Der1, Base>
{
static int foo() { std::cout << "Der1::foo()" << std::endl; }
};


struct Der2 : CRTPFooInjector<Der2, Base>
{
static int foo() { std::cout << "Der2::foo()" << std::endl; }
};


struct Der12 : CRTPFooInjector<Der12, Der1, Der2>
{
static int foo() { std::cout << "Der12::foo()" << std::endl; }
};

Now instead of your normal hierarchy Base <- Der1 <- Der12 you have a slightly different one Injector <- Base <- Injector <- Der1 <- Injector <- Der12, but this should be transparent for most users.

If you need to mix in some virtual base classes:

template <typename T>
struct Virtual : virtual T
{
};

struct Der1 : CRTPFooInjector<Der1, Virtual<Base>> ...

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.

Alternative to c++ static virtual methods

You can make Base be a class template that takes its function pointers from its template argument:

extern "C" {
struct CStruct
{
void (*funA)(int, char const*);
int (*funB)(void);
};
}

template <typename T>
class Base
{
public:
CStruct myStruct;
void FillPointers() {
myStruct.funA = &T::myFunA;
myStruct.funB = &T::myFunB;
}
Base() {
FillPointers();
}
};

Then, define your derived classes to descend from an instantiation of Base using each derived class as the template argument:

class Derived1: public Base<Derived1>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 0; }
};

class Derived2: public Base<Derived2>
{
public:
static void myFunA(int, char const*) { }
static int myFunB() { return 1; }
};

int main() {
Derived1 d1;
d1.myStruct.funA(0, 0);
d1.myStruct.funB();
Derived2 d2;
d2.myStruct.funA(0, 0);
d2.myStruct.funB();
}

That technique is known as the curiously recurring template pattern. If you neglect to implement one of the functions in a derived class, or if you change the function signature, you'll get a compilation error, which is exactly what you'd expect to get if you neglected to implement one of the pure virtual functions from your original plan.

The consequence of this technique, however, is that Derived1 and Derived2 do not have a common base class. The two instantiations of Base<> are not related in any way, as far as the type system is concerned. If you need them to be related, then you can introduce another class to serve as the base for the template, and then put the common things there:

class RealBase
{
public:
CStruct myStruct;
};

template <typename T>
class Base: public RealBase
{
// ...
};

int main()
RealBase* b;
Derived1 d1;
b = &d1;
b->myStruct.funA(0, 0);
b->myStruct.funB();
Derived2 d2;
b = &d2;
b->myStruct.funA(0, 0);
b->myStruct.funB();
}

Beware: Static member functions are not necessarily compatible with ordinary function pointers. In my experience, if the compiler accepts the assignment statements shown above, then you can at least be confident that they're compatible for that compiler. This code isn't portable, but if it works on all the platforms you need to support, then you might consider it "portable enough."

Is it possible to declare a virtual static constant value in a C++ class?

A static method cannot be virtual, and no data members can be virtual.

But you can hide static fields in derived classes and use a virtual method to return them.

class A
{
public:
static const int ID = 0;
virtual int getID() { return A::ID; }
};
class B : A
{
public:
static const int ID = 1;
virtual int getID() { return B::ID; }
};

Alternative:

class A
{
public:
A(int id = 0) : ID(id) {}
const int ID;
getID() { return ID; }
};
class B : public A
{
public:
B() : A(1) {}
};


Related Topics



Leave a reply



Submit