Это невозможно, но это только потому, что упущение. Это не то, что «не имеет смысла», как утверждают многие люди. Чтобы было ясно, я говорю о чем-то вроде этого:
struct Base {
static virtual void sayMyName() {
cout << "Base\n";
}
};
struct Derived : public Base {
static void sayMyName() override {
cout << "Derived\n";
}
};
void foo(Base *b) {
b->sayMyName();
Derived::sayMyName(); // Also would work.
}
Это 100% то, что может быть реализовано (просто нет), и я бы сказал, что это полезно.
Рассмотрим, как работают обычные виртуальные функции. Удалите static
s и добавьте еще кое-что, и мы получим:
struct Base {
virtual void sayMyName() {
cout << "Base\n";
}
virtual void foo() {
}
int somedata;
};
struct Derived : public Base {
void sayMyName() override {
cout << "Derived\n";
}
};
void foo(Base *b) {
b->sayMyName();
}
Это работает нормально, и в основном происходит то, что компилятор создает две таблицы, называемые VTables, и присваивает индексы виртуальным функциям, подобным этому
enum Base_Virtual_Functions {
sayMyName = 0;
foo = 1;
};
using VTable = void*[];
const VTable Base_VTable = {
&Base::sayMyName,
&Base::foo
};
const VTable Derived_VTable = {
&Derived::sayMyName,
&Base::foo
};
Затем каждый класс с виртуальными функциями дополняется другим полем, которое указывает на его VTable, поэтому компилятор в основном меняет их так:
struct Base {
VTable* vtable;
virtual void sayMyName() {
cout << "Base\n";
}
virtual void foo() {
}
int somedata;
};
struct Derived : public Base {
VTable* vtable;
void sayMyName() override {
cout << "Derived\n";
}
};
Тогда что же на самом деле происходит, когда вы звоните b->sayMyName()
? В основном это:
b->vtable[Base_Virtual_Functions::sayMyName](b);
(первый параметр становится this
.)
Хорошо, хорошо, как это будет работать со статическими виртуальными функциями? В чем разница между статическими и нестатическими функциями-членами? Единственное отличие состоит в том, что последний получает указатель this
.
Мы можем сделать то же самое со статическими виртуальными функциями - просто удалите указатель this
.
b->vtable[Base_Virtual_Functions::sayMyName]();
Это может поддерживать оба синтаксиса:
b->sayMyName(); // Prints "Base" or "Derived"...
Base::sayMyName(); // Always prints "Base".
Так что игнорируйте всех скептиков. Это имеет смысл. Почему тогда не поддерживается? Я думаю, это потому, что это дает очень мало пользы и может даже немного запутать.
Единственное техническое преимущество по сравнению с обычной виртуальной функцией заключается в том, что вам не нужно передавать this
в функцию, но я не думаю, что это могло бы повлиять на производительность.
Это означает, что у вас нет отдельной статической и нестатической функции для случаев, когда у вас есть экземпляр, и когда у вас нет экземпляра, но также может сбить с толку тот факт, что он действительно «виртуальный» когда вы используете вызов экземпляра.