C ++ статичних віртуальних членів?


140

Чи можливо в C ++ мати функцію члена, яка є staticі virtual? Мабуть, немає простого способу зробити це ( static virtual member();помилка компіляції), але чи є принаймні спосіб досягти такого ж ефекту?

IE:

struct Object
{
     struct TypeInformation;

     static virtual const TypeInformation &GetTypeInformation() const;
};

struct SomeObject : public Object
{
     static virtual const TypeInformation &GetTypeInformation() const;
};

Має сенс використовувати GetTypeInformation()і в екземплярі ( object->GetTypeInformation()), і в класі ( SomeObject::GetTypeInformation()), що може бути корисно для порівнянь і життєво важливим для шаблонів.

Єдиний спосіб, про який я можу подумати, полягає у написанні двох функцій / функції та константи, на клас чи використання макросів

Будь-які інші рішення?


12
Лише побічний коментар: статичні методи не виконуються в жодному випадку, що означає, що у них немає неявного цього вказівника. При цьому, constпідпис в методі позначає неявний thisпокажчик як постійний і не може бути застосований до статичних методів, оскільки у них відсутній неявний параметр.
Девід Родрігес - дрибес

2
@cvb: Я серйозно перегляну, замінивши ваш приклад кодом, який не передбачає роздумів. Зараз ви начебто плутаєте два окремих (хоча й пов’язаних) питання. Так, і я знаю, що минуло 5 з половиною років, як ти це попросив.
einpoklum

Однією з особливостей, яка тут чітко потрібна, є те, щоб компілятор перевіряв, чи кожен об'єкт в ієрархії реалізує певний інтерфейс (де один або кілька методів є статичними). В основному, чиста віртуальна перевірка статичного методу має багато сенсу, оскільки якщо ви забудете додати статичний метод, тоді компілятор повинен помилитися. Віртуальна не є ключовим словом тут, це більш абстрактно, яке, як буває, є синонімом C ++, за винятком цього конкретного випадку. На жаль, наразі це не можна зробити із C ++.
xryl669

Відповіді:


75

Ні, немає ніякого способу зробити це, адже що буде, коли ти подзвониш Object::GetTypeInformation()? Він не може знати, яку похідну версію класу потрібно викликати, оскільки з нею не пов'язаний жоден об'єкт.

Для правильної роботи вам доведеться зробити нестатичну віртуальну функцію; якщо ви також хочете мати можливість викликати версію конкретного похідного класу не практично без об'єкта об'єкта, вам також доведеться надати другу надмірну статичну невіртуальну версію.


8
Якщо ви ставитеся до статичного класу (або статичних членів класів) як до однотонного, все стає очевидним - у вашому випадку слід просто викликати Object :: GetTypeInformation так само, як викликати звичайний віртуальний метод в екземплярі базового класу . (Звичайно, якщо C ++ підтримував віртуальні статичні методи)
Spook

13
Це абсолютно доречний аргумент. Якщо ви використовуєте клас замість об'єкта, то, природно, буде використана версія цього класу, а не віртуальна диспетчера. Нічого нового там немає.
Дедуплікатор

54

Багато хто каже, що це неможливо, я б пішов на крок далі і сказав, що це не має сенсу.

Статичний член - це те, що не стосується жодного екземпляра, лише класу.

Віртуальний член - це те, що не відноситься безпосередньо до будь-якого класу, лише до екземпляра.

Отже, статичний віртуальний член буде чимось, що не стосується жодного екземпляра чи будь-якого класу.


42
Це абсолютно важливо для мов, де класи - це значення першого класу - наприклад, Delphi має це, а також має "статичні віртуальні" методи.
Павло Мінаєв

4
Саме так. "Віртуальна функція" - це (за визначенням) функція, яка динамічно пов'язана , тобто вона вибирається під час виконання в залежності від динамічного типу даного об'єкта. Отже, немає об'єкта = немає віртуального виклику.
Кос

7
Я також вважаю, що статичні віртуали мають сенс. Можна було б визначити класи інтерфейсу та включити статичні методи, які мають бути реалізовані у похідному класі.
bkausbk

34
Це не настільки значуще для static virtualметоду, але static чистий virtual метод дуже значущий в інтерфейсі.
Брет Кунз

4
Це абсолютно сенс мати static const string MyClassSillyAdditionalName.
einpoklum

23

Я натрапив на цю проблему днями: у мене були кілька класів, повних статичних методів, але я хотів використовувати успадкування та віртуальні методи та зменшити повторення коду. Моє рішення було:

Замість статичних методів використовуйте синглтон з віртуальними методами.

Іншими словами, кожен клас повинен містити статичний метод, який ви викликаєте, щоб отримати вказівник на один спільний екземпляр класу. Ви можете зробити справжніх конструкторів приватними або захищеними, щоб зовнішній код не міг нею користуватися, створюючи додаткові екземпляри.

На практиці використання синглтона дуже схоже на використання статичних методів, за винятком того, що ви можете скористатися успадкуванням та віртуальними методами.


Це коштуватиме мені продуктивності - якщо тільки компілятор не може бути впевнений, що: 1. Це насправді сингл і 2. Нічого не успадковується від цього, я не думаю, що він може оптимізувати весь накладний виїзд.
einpoklum

Якщо виконання подібних речей вас турбує, тоді C #, мабуть, неправильна мова для вас.
Nate CK

3
Ах, хороший пункт. Очевидно, що минув час, коли я подумав про це, коли написав це у 2009 році. Дозвольте сказати іншим способом: тоді, якщо ця робота з виконанням турбує вас, то, можливо, вам слід повністю уникати використання спадщини. Плакат спеціально попросив віртуальні методи, тому дивно, що ви приходите сюди скаржитися на накладні витрати віртуальних методів.
Нейт СК

15

Можливо!

Але що саме можливо, давайте звузимо. Люди часто хочуть якусь "статичну віртуальну функцію" через дублювання коду, необхідного для можливості викликати ту саму функцію за допомогою статичного виклику "SomeDerivedClass :: myfunction ()" і поліморфного виклику "base_class_pointer-> myfunction ()". "Юридичний" метод дозволу такої функціональності - це дублювання визначень функції:

class Object
{
public:
    static string getTypeInformationStatic() { return "base class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
}; 
class Foo: public Object
{
public:
    static string getTypeInformationStatic() { return "derived class";}
    virtual string getTypeInformation() { return getTypeInformationStatic(); }
};

Що робити, якщо базовий клас має велику кількість статичних функцій, і похідний клас повинен перекрити кожну з них, і хтось забув надати визначення, що дублюється, для віртуальної функції. Правильно, ми отримаємо якусь дивну помилку під час виконання, яку важко відстежити. Тому що дублювання коду - це погана річ. Наступне намагається вирішити цю проблему (і я хотів би заздалегідь сказати, що вона повністю безпечна для типу і не містить жодної чорної магії, як-от typeid's або dynamic_cast's :)

Отже, ми хочемо надати лише одне визначення getTypeInformation () для похідного класу, і очевидно, що це повинно бути визначенням статичногофункцію, тому що неможливо викликати "SomeDerivedClass :: getTypeInformation ()", якщо getTypeInformation () є віртуальним. Як можна назвати статичну функцію похідного класу через вказівник на базовий клас? З vtable це неможливо, оскільки vtable зберігає вказівники лише на віртуальні функції, і оскільки ми вирішили не використовувати віртуальні функції, ми не можемо змінювати vtable на нашу користь. Потім, щоб мати доступ до статичної функції для похідного класу через вказівник на базовий клас, ми повинні якось зберігати тип об'єкта в його базовому класі. Один із підходів полягає в тому, щоб зробити базовий клас шаблонованим, використовуючи "цікаво повторюваний шаблон шаблону", але це не доречно тут, і ми будемо використовувати техніку під назвою "видалити стирання":

class TypeKeeper
{
public:
    virtual string getTypeInformation() = 0;
};
template<class T>
class TypeKeeperImpl: public TypeKeeper
{
public:
    virtual string getTypeInformation() { return T::getTypeInformationStatic(); }
};

Тепер ми можемо зберігати тип об'єкта в базовому класі "Object" зі змінною "зберігач":

class Object
{
public:
    Object(){}
    boost::scoped_ptr<TypeKeeper> keeper;

    //not virtual
    string getTypeInformation() const 
    { return keeper? keeper->getTypeInformation(): string("base class"); }

};

У похідному класі зберігач повинен бути ініціалізований під час побудови:

class Foo: public Object
{
public:
    Foo() { keeper.reset(new TypeKeeperImpl<Foo>()); }
    //note the name of the function
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

Додамо синтаксичний цукор:

template<class T>
void override_static_functions(T* t)
{ t->keeper.reset(new TypeKeeperImpl<T>()); }
#define OVERRIDE_STATIC_FUNCTIONS override_static_functions(this)

Тепер декларації нащадків виглядають так:

class Foo: public Object
{
public:
    Foo() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "class for proving static virtual functions concept"; }
};

class Bar: public Foo
{
public:
    Bar() { OVERRIDE_STATIC_FUNCTIONS; }
    static string getTypeInformationStatic() 
    { return "another class for the same reason"; }
};

використання:

Object* obj = new Foo();
cout << obj->getTypeInformation() << endl;  //calls Foo::getTypeInformationStatic()
obj = new Bar();
cout << obj->getTypeInformation() << endl;  //calls Bar::getTypeInformationStatic()
Foo* foo = new Bar();
cout << foo->getTypeInformation() << endl; //calls Bar::getTypeInformationStatic()
Foo::getTypeInformation(); //compile-time error
Foo::getTypeInformationStatic(); //calls Foo::getTypeInformationStatic()
Bar::getTypeInformationStatic(); //calls Bar::getTypeInformationStatic()

Переваги:

  1. менше дублювання коду (але ми повинні викликати OVERRIDE_STATIC_FUNCTIONS у кожному конструкторі)

Недоліки:

  1. OVERRIDE_STATIC_FUNCTIONS у кожному конструкторі
  2. пам'ять та продуктивність накладні
  3. підвищена складність

Відкриті питання:

1) існують різні назви статичних і віртуальних функцій, як тут вирішити двозначність?

class Foo
{
public:
    static void f(bool f=true) { cout << "static";}
    virtual void f() { cout << "virtual";}
};
//somewhere
Foo::f(); //calls static f(), no ambiguity
ptr_to_foo->f(); //ambiguity

2) як неявно викликати OVERRIDE_STATIC_FUNCTIONS всередині кожного конструктора?


+1 для зусиль, хоча я не впевнений, що це елегантніше, ніж просто делегувати функціональність синглу з віртуальними методами.
einpoklum

1
@einpoklum, я можу придумати ситуацію, коли це може бути кращим. Припустимо, у нас багато клієнтського коду, який вже викликає статичні методи. Перехід від статичних методів до синглтонних з віртуальними методами потребує змін у коді клієнта, тоді як рішення, представлене вище, є неінвазивним.
Альск

"віртуальне" ключове слово не потрібно для "Foo :: getTypeInformation" та "TypeKeeperImpl :: getTypeInformation".
bartolo-otrit

12

Хоча Алск вже дав досить детальну відповідь, я хотів би додати альтернативу, оскільки вважаю, що його розширена реалізація є надто складною.

Ми починаємо з абстрактного базового класу, який забезпечує інтерфейс для всіх типів об'єктів:

class Object
{
public:
    virtual char* GetClassName() = 0;
};

Тепер нам потрібна реальна реалізація. Але щоб уникнути необхідності писати як статичний, так і віртуальний методи, у нас будуть фактичні класи об'єктів успадковувати віртуальні методи. Це, очевидно, працює лише в тому випадку, якщо базовий клас знає, як отримати доступ до функції статичного члена. Тому нам потрібно використовувати шаблон і передати йому фактичне ім'я класу об'єктів:

template<class ObjectType>
class ObjectImpl : public Object
{
public:
    virtual char* GetClassName()
    {
        return ObjectType::GetClassNameStatic();
    }
};

Нарешті нам потрібно реалізувати наші реальні об’єкти. Тут нам потрібно лише реалізувати статичну функцію члена, функції віртуальних членів успадковуватимуться від шаблонного класу ObjectImpl, інстанціюється іменем похідного класу, тому він отримає доступ до своїх статичних членів.

class MyObject : public ObjectImpl<MyObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "MyObject";
    }
};

class YourObject : public ObjectImpl<YourObject>
{
public:
    static char* GetClassNameStatic()
    {
        return "YourObject";
    }
};

Додамо код для перевірки:

char* GetObjectClassName(Object* object)
{
    return object->GetClassName();
}

int main()
{
    MyObject myObject;
    YourObject yourObject;

    printf("%s\n", MyObject::GetClassNameStatic());
    printf("%s\n", myObject.GetClassName());
    printf("%s\n", GetObjectClassName(&myObject));
    printf("%s\n", YourObject::GetClassNameStatic());
    printf("%s\n", yourObject.GetClassName());
    printf("%s\n", GetObjectClassName(&yourObject));

    return 0;
}

Додаток (12 січня 2019 р.):

Замість використання функції GetClassNameStatic () ви можете також визначити ім'я класу як статичний член, навіть "вбудований", який IIRC працює з C ++ 11 (не лякайтеся всіх модифікаторів :)):

class MyObject : public ObjectImpl<MyObject>
{
public:
    // Access this from the template class as `ObjectType::s_ClassName` 
    static inline const char* const s_ClassName = "MyObject";

    // ...
};

11

Можливо. Зробіть дві функції: статичну та віртуальну

struct Object{     
  struct TypeInformation;
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain1();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain1();
  }
protected:
  static const TypeInformation &GetTypeInformationMain1(); // Main function
};

struct SomeObject : public Object {     
  static  const TypeInformation &GetTypeInformationStatic() const 
  { 
      return GetTypeInformationMain2();
  }
  virtual const TypeInformation &GetTypeInformation() const
  { 
      return GetTypeInformationMain2();
  }
protected:
  static const TypeInformation &GetTypeInformationMain2(); // Main function
};

4
Також статичні методи не можуть бути const. Це просто не має сенсу, який екземпляр вони не збираються мутувати?
Девід Родрігес - дрибес

1
Це здебільшого просто дублювання коду. Ідея полягає в тому, щоб підкласи повинні мати лише статичний член const, а не мати код, що має доступ до нього.
einpoklum

8

Ні, це неможливо, оскільки статичним функціям членів не вистачає thisвказівника. А статичні члени (як функції, так і змінні) насправді не є членами класу самі по собі. Вони просто викликаються ClassName::memberі дотримуються специфікаторів доступу до класу. Їх зберігання визначено десь поза класом; сховище не створюється кожного разу, коли інстанціюється об’єкт класу. Покажчики на учасників класу є особливими за семантикою та синтаксисом. Вказівник на статичний член є нормальним у всіх відношеннях.

віртуальні функції в класі потребують thisвказівника, і він дуже поєднаний з класом, отже, вони не можуть бути статичними.


1
Тільки нестатичні функції потребують this вказівника. статичні функції не характерні для екземпляра, і вони не потребуватимуть його. Отже - це не причина, що віртуальних статичних членів неможливо.
einpoklum

7

Ну, досить пізня відповідь, але це можливо, використовуючи цікаво повторюваний шаблон шаблону. Ця стаття у Вікіпедії містить необхідну інформацію, а також приклад, при якому статичний поліморфізм - це те, про що ви просите.


3

Я думаю, що те, що ти намагаєшся зробити, можна зробити через шаблони. Я намагаюся тут читати між рядків. Що ви намагаєтеся зробити, це викликати метод з якогось коду, де він викликає похідну версію, але абонент не визначає, який клас. Приклад:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

void Try()
{
    xxx::M();
}

int main()
{
    Try();
}

Ви хочете спробувати () викликати бар-версію M, не вказуючи Bar. Те, як ви зробите це для статики, - це використовувати шаблон. Тож змініть це так:

class Foo {
public:
    void M() {...}
};

class Bar : public Foo {
public:
    void M() {...}
};

template <class T>
void Try()
{
    T::M();
}

int main()
{
    Try<Bar>();
}

1
Якщо ви вставте пробіл у коді 4, ви зможете автоматично його форматувати. Як варіант, я вважаю, що ви можете використовувати задній галочку, щоб досягти тієї ж мети.
холліда

1
Це явне я пропустив. Дякую. Тим НЕ менше, лобкові члени дивно.
allesblinkt

M () не є статичною функцією. як це називається T :: M ()?
DDukDDak99

3

Ні, статична функція члена не може бути віртуальною .since віртуальна концепція вирішується під час виконання за допомогою vptr, а vptr не є статичним членом class.due, щоб статична функція члена не могла отримати доступ до vptr, тому статичний член може не буде віртуальним.


2
Тільки для конкретних примірників віртуальних методів потрібна версія vtable. У вас може бути статичний vtable для одного класу. І якщо ви хочете, щоб про них знали екземпляри, просто вкажіть з vtable в екземплярі також статику vtable statics.
einpoklum

2

Це неможливо, але це лише тому, що упущення. Це не те, що "не має сенсу", як здається, багато людей. Щоб було зрозуміло, я говорю про щось подібне:

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і додайте ще деякі речі, і ми маємо:

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до функції, але я не думаю, що це може змінити продуктивність.

Це означає, що ви не маєте окремої статичної та нестатичної функції для випадків, коли у вас є екземпляр, і коли у вас немає екземпляра, але також може бути заплутано те, що це дійсно "віртуально", коли ви використовуєте виклик екземпляра.


0

Ні, це неможливо, оскільки статичні члени пов'язані під час компіляції, а віртуальні члени пов'язані під час виконання.


0

По-перше, відповіді вірні, що те, що вимагає ОП, є суперечливістю: віртуальні методи залежать від типу виконання екземпляра; статичні функції конкретно не залежать від екземпляра - лише від типу. З огляду на це, має сенс статичні функції повертати щось певне до типу. Наприклад, у мене було сімейство класів MouseTool для моделі State, і я почав мати кожен з них статичну функцію, повертаючи модифікатор клавіатури, що йде разом з нею; Я використовував ті статичні функції у фабричній функції, які зробили правильний екземпляр MouseTool. Ця функція перевіряла стан миші на MouseToolA :: keyboardModifier (), MouseToolB :: keyboardModifier () тощо, а потім інстанціювала відповідний. Звичайно, пізніше я хотів перевірити, чи правильно держава, тому я хотів написати щось на кшталт "

Отже, якщо вам здається, що цього ви хочете, ви можете скористатися рішенням. Все-таки я розумію бажання мати статичні методи, а потім викликати їх динамічно на основі динамічного типу екземпляра. Я думаю, що шаблон відвідувачів може дати вам те, що ви хочете. Це дає вам те, що ви хочете. Це трохи додаткового коду, але він може бути корисним для інших відвідувачів.

Дивіться: http://en.wikipedia.org/wiki/Visitor_pattern для фону.

struct ObjectVisitor;

struct Object
{
     struct TypeInformation;

     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v);
};

struct SomeObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

struct AnotherObject : public Object
{
     static TypeInformation GetTypeInformation();
     virtual void accept(ObjectVisitor& v) const;
};

Потім для кожного конкретного об'єкта:

void SomeObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // The compiler statically picks the visit method based on *this being a const SomeObject&.
}
void AnotherObject::accept(ObjectVisitor& v) const {
    v.visit(*this); // Here *this is a const AnotherObject& at compile time.
}

а потім визначте базового відвідувача:

struct ObjectVisitor {
    virtual ~ObjectVisitor() {}
    virtual void visit(const SomeObject& o) {} // Or = 0, depending what you feel like.
    virtual void visit(const AnotherObject& o) {} // Or = 0, depending what you feel like.
    // More virtual void visit() methods for each Object class.
};

Потім конкретний відвідувач, який вибирає відповідну статичну функцію:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) {
        result = SomeObject::GetTypeInformation();
    }
    virtual void visit(const AnotherObject& o) {
        result = AnotherObject::GetTypeInformation();
    }
    // Again, an implementation for each concrete Object.
};

нарешті, використовуйте його:

void printInfo(Object& o) {
    ObjectVisitorGetTypeInfo getTypeInfo;
    Object::TypeInformation info = o.accept(getTypeInfo).result;
    std::cout << info << std::endl;
}

Примітки:

  • Скромність залишилася як вправа.
  • Ви повернули посилання зі статики. Якщо у вас немає одиноких, це сумнівно.

Якщо ви хочете уникнути помилок копіювання та вставки, коли один із способів відвідування викликає неправильну статичну функцію, ви можете використовувати шаблонну функцію помічника (яка сама по собі не може бути віртуальною), щоб ваш відвідувач мав такий шаблон:

struct ObjectVisitorGetTypeInfo {
    Object::TypeInformation result;
    virtual void visit(const SomeObject& o) { doVisit(o); }
    virtual void visit(const AnotherObject& o) { doVisit(o); }
    // Again, an implementation for each concrete Object.

  private:
    template <typename T>
    void doVisit(const T& o) {
        result = T::GetTypeInformation();
    }
};

віртуальні статичні методи, якби вони існували, не залежатимуть ні від чого в екземплярі - але екземпляр повинен знати свій тип, щоб викликати їх. Це може бути розроблено компілятором (наприклад, за допомогою якоїсь однієї структури даних по одному класу з покажчиками на віртуальні статичні методи та члени.) Це, звичайно, не суперечить термінам.
einpoklum

Чи це суперечність з точки зору чи ні, це питання семантики. Можна уявити C ++, що дозволяє викликати статику з екземпляра (наприклад, Foo foo; ... foo::bar();замість Foo::bar();). Це не на відміну, decltype(foo)::bar();але це знову буде статично пов'язаним. Підхід для відвідувачів представляється розумним способом отримати таку поведінку, не роблячи статичний метод віртуальним методом const.
Бен

0

За допомогою c ++ ви можете використовувати статичне успадкування методом crt. Наприклад, він широко використовується у віконному шаблоні atl & wtl.

Дивіться https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

Щоб бути простим, у вас є клас, який шаблонується від себе, як клас myclass: public myancestor. З цього моменту клас mancestor тепер може викликати вашу статичну функцію T :: YourImpl.


-1

Можливо, ви можете спробувати моє рішення нижче:

class Base {
public:
    Base(void);
    virtual ~Base(void);

public:
    virtual void MyVirtualFun(void) = 0;
    static void  MyStaticFun(void) { assert( mSelf != NULL); mSelf->MyVirtualFun(); }
private:
    static Base* mSelf;
};

Base::mSelf = NULL;

Base::Base(void) {
    mSelf = this;
}

Base::~Base(void) {
    // please never delete mSelf or reset the Value of mSelf in any deconstructors
}

class DerivedClass : public Base {
public:
    DerivedClass(void) : Base() {}
    ~DerivedClass(void){}

public:
    virtual void MyVirtualFun(void) { cout<<"Hello, it is DerivedClass!"<<endl; }
};

int main() {
    DerivedClass testCls;
    testCls.MyStaticFun(); //correct way to invoke this kind of static fun
    DerivedClass::MyStaticFun(); //wrong way
    return 0;
}

Так, я знаю, 4 роки. Пояснення показника -score для тих, хто не хоче читати код в таких деталях. Base::mSelfвідноситься до НАЙБІЛЬШЕ побудованого екземпляра будь-якого похідного класу, навіть якщо цей екземпляр був знищений . так class D1 : public Base ...; class D2 : public Base ...; ...; D1* pd1 = new D1(); D2* pd2 = new D2(); pd1->MyStaticFun(); /* calls D2::MyVirtualFun() */ delete pd2; pd1->MyStaticFun(); /* calls via deleted pd2 */Який НЕ є тим, що потрібно.
Джессі Чісгольм

-3

Як уже говорили інші, є 2 важливі відомості:

  1. немає thisпокажчика під час здійснення статичного виклику функції та
  2. що thisвказує покажчик на структуру , де віртуальна таблиця, або перетворювач, які використовуються для перегляду , який метод виконання для виклику.

Статична функція визначається під час компіляції.

Я показав цей приклад коду в C ++ статичних членів у класі ; це показує, що ви можете викликати статичний метод з нульовим вказівником:

struct Foo
{
    static int boo() { return 2; }
};

int _tmain(int argc, _TCHAR* argv[])
{
    Foo* pFoo = NULL;
    int b = pFoo->boo(); // b will now have the value 2
    return 0;
}

6
Технічно це невизначена поведінка. Ви не можете визначити нульовий покажчик з будь-якої причини. Єдине, що можна зробити з нульовим вказівником, це: а) призначити йому інший покажчик і б) порівняти його з іншим вказівником.
KeithB

1
Крім того, ви можете порівнювати його лише за рівність (або нерівність_ з іншим вказівником, не впорядковуючи. Тобто p < null, p >= nullі т . Д. - теж не визначено)
Павло Мінаєв,

1
@KeithB - ​​для повноти можна також сміливо викликати видалення за допомогою нульового вказівника.
Стів Роу
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.