C ++静的仮想メンバー?


140

C ++ではstaticandとなるメンバー関数を持つことは可能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())の両方で使用することは意味があります。これは、比較に役立ち、テンプレートに不可欠です。

私が考えることができる唯一の方法は、クラスごとに、またはマクロを使用して、関数と定数の2つの関数を記述することです。

他の解決策はありますか?


12
余談ですが、静的メソッドはどのインスタンスでも実行されません。つまり、暗黙のthisポインターがないということです。つまりconst、メソッドのシグネチャは、暗黙のthisポインターに定数としてフラグを立て、暗黙のパラメーターがないため静的メソッドに適用できません。
デビッドロドリゲス-ドリベス09

2
@cvb:例をリフレクションを含まないコードに置き換えることを真剣に考え直します。現在の方法では、2つの個別の(関連している)問題を混同しています。はい、あなたが質問してから5年半経っているのはわかっています。
einpoklum 2015

ここで暗黙的に必要な機能の1つは、階層内の各オブジェクトが特定のインターフェース(1つ以上のメソッドが静的である)を実装していることをコンパイラーにチェックさせることです。基本的に、静的メソッドの純粋な仮想チェックは非常に理にかなっています。静的メソッドを追加し忘れると、コンパイラエラーになるはずです。virtualはここではキーワードではありませんが、この特定の場合を除いて、C ++の同義語のような、より抽象的なものです。残念ながら、現在C ++ではできません。
xryl669 2017年

回答:


75

いいえ、電話をかけるとObject::GetTypeInformation()どうなりますか?関連付けられているオブジェクトがないため、どの派生クラスバージョンを呼び出すかを知ることができません。

正しく機能させるには、それを非静的仮想関数にする必要があります。特定の派生クラスのバージョンをオブジェクトインスタンスなしで非仮想的に呼び出すこともできるようにする場合は、2番目の冗長な静的非仮想バージョンも提供する必要があります。


8
静的クラス(またはクラスの静的メンバー)をシングルトンと考えると、すべてが明らかになります。この場合、単純にObject :: GetTypeInformationを呼び出す必要があります-基本クラスインスタンスで通常の仮想メソッドを呼び出すのと同じ方法です。(もちろん、C ++が仮想静的メソッドをサポートしている場合
スプーク

13
それは完全に疑わしい議論です。オブジェクトの代わりにクラスを使用する場合、virtual-dispatchを実行する代わりに、そのクラスのバージョンを当然使用します。そこに新しいものはありません。
Deduplicator

54

多くの人がそれは不可能だと言います、私はさらに一歩進んでそれは意味がないと言います。

静的メンバーは、インスタンスに関係なく、クラスにのみ関係するものです。

仮想メンバーは、クラスには直接関係せず、インスタンスにのみ関係するものです。

したがって、静的仮想メンバーは、インスタンスやクラスに関連しないものになります。


42
これは、クラスがファーストクラスの値である言語では完全に意味があります。たとえば、Delphiにはそれがあり、「静的仮想」メソッドもあります。
Pavel Minaev 2009年

4
丁度。「仮想関数」は(定義により)動的にリンクされる関数です。つまり、指定されたオブジェクトの動的なタイプに応じて、実行時に選択されます。したがって、オブジェクトなし=仮想呼び出しはありません。
Kos、

7
また、静的仮想は意味があると思います。インターフェースクラスを定義し、派生クラスに実装する必要のある静的メソッドを含めることは可能です。
bkausbk 2013年

34
static virtualメソッドにとってはそれほど意味がありませんが、static 純粋な virtualメソッドはインターフェースにおいて非常に意味があります。
Bret Kuhns 2013

4
を持つことは完全に意味がありstatic const string MyClassSillyAdditionalNameます。
einpoklum 2014

23

先日、この問題に遭遇しました。静的メソッドでいっぱいのクラスがいくつかありましたが、継承メソッドと仮想メソッドを使用して、コードの繰り返しを減らしたかったのです。私の解決策は:

静的メソッドを使用する代わりに、仮想メソッドでシングルトンを使用します。

つまり、各クラスには、クラスの単一の共有インスタンスへのポインタを取得するために呼び出す静的メソッドが含まれている必要があります。追加のインスタンスを作成することによって外部コードが悪用しないように、真のコンストラクターをプライベートまたは保護することができます。

実際には、シングルトンの使用は静的メソッドの使用とよく似ていますが、継承と仮想メソッドを利用できる点が異なります。


1.コンパイラーは実際にはシングルトンであり、2。何も継承されていないため、オーバーヘッドをすべて最適化できるとは思えません。
einpoklum 2015

この種のパフォーマンスが気になる場合、C#はおそらく間違った言語です。
Nate CK、

3
ああ、良い点。明らかに、2009年にそれを書いてから、これについて考えてからしばらく時間が経っています。言い換えると、この種類のパフォーマンスが気になる場合は、継承の使用を完全に回避する必要があります。ポスターは仮想メソッドを具体的に求めているため、仮想メソッドのオーバーヘッドについて不平を言うためにここに来るのは奇妙です。
Nate CK

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やdynamic_castのようなブラックマジックが含まれていないことを前に伝えておきたいと思います。)

したがって、派生クラスごとにgetTypeInformation()の定義を1つだけ提供する必要があり、それがstaticの定義でなければならないことは明らかです。getTypeInformation()が仮想の場合、 "SomeDerivedClass :: getTypeInformation()"を呼び出すことができないため、関数。基本クラスへのポインターを介して、派生クラスの静的関数をどのように呼び出すことができますか?vtableは仮想関数へのポインターのみを格納するため、vtableでは不可能です。仮想関数を使用しないことにしたため、vtableを変更することはできません。次に、基本クラスへのポインターを介して派生クラスの静的関数にアクセスできるようにするには、なんらかの方法でその基本クラス内のオブジェクトのタイプを格納する必要があります。1つのアプローチは、「不思議な繰り返しのテンプレートパターン」を使用して基本クラスをテンプレート化することですが、ここでは適切ではなく、「型消去」と呼ばれる手法を使用します。

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

これで、変数「キーパー」を使用して、基本クラス「オブジェクト」内にオブジェクトのタイプを格納できます。

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 2014

1
@einpoklum、これが望ましい状況だと思います。静的メソッドをすでに呼び出す多くのクライアントコードがあるとします。静的メソッドから仮想メソッドを使用したシングルトンに切り替えるには、クライアントコードを変更する必要がありますが、上記のソリューションは非侵襲的です。
Alsk、2014

「仮想」キーワードは、「Foo :: getTypeInformation」および「TypeKeeperImpl :: getTypeInformation」には必要ありません。
bartolo-otrit 2014

12

Alskはすでにかなり詳細な回答を提供していますが、彼の拡張された実装は複雑すぎると思うので、代替案を追加したいと思います。

すべてのオブジェクトタイプのインターフェイスを提供する抽象基本クラスから始めます。

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;
}

補遺(2019年1月12日):

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

可能です。静的と仮想の2つの関数を作成する

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 2014

8

いいえ、これは不可能thisです。静的メンバー関数にはポインターがないからです。また、静的メンバー(関数と変数の両方)は、それ自体は実際にはクラスメンバーではありません。それらはたまたまによって呼び出されClassName::member、クラスアクセス指定子に従います。それらのストレージはクラスの外のどこかで定義されています。ストレージは、クラスのオブジェクトをインスタンス化するたびに作成されるわけではありません。クラスメンバーへのポインターは、セマンティクスと構文が特別です。静的メンバーへのポインターは、あらゆる点で通常のポインターです。

クラス内の仮想関数はthisポインターを必要とし、クラスに非常に結合されているため、静的にすることはできません。


1
非静的関数のみがthis ポインターを必要とします。静的関数はインスタンスに固有ではなく、それを必要としません。したがって、それが仮想静的メンバーが不可能な理由ではありません。
アインポクルム2016

7

まあ、かなり遅い答えですが、奇妙に繰り返されるテンプレートパターンを使用することは可能です。このウィキペディアの記事には必要な情報が含まれており、静的多型の例も求められています。


3

私はあなたがやろうとしていることはテンプレートを通してできると思います。ここで行間を読み込もうとしています。あなたがやろうとしていることは、いくつかのコードからメソッドを呼び出すことです。この場合、メソッドは派生バージョンを呼び出しますが、呼び出し元はどのクラスを指定していません。例:

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

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

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

int main()
{
    Try();
}

Try()で、バーを指定せずにバーバージョンのMを呼び出す必要があります。静的に対してそれを行う方法は、テンプレートを使用することです。次のように変更してください:

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スペースインデントすると、自動的にフォーマットされます。または、バックティックを使用して同じ目的をインラインで達成できると思います。
コリダ2009年

1
これは私が見逃した明らかです。ありがとうございました。それでも、陰部のメンバーは奇妙です。
allesblinkt

M()は静的関数ではありません。どのようにT :: M()と呼ばれますか?
DDukDDak99

3

いいえ、静的メンバー関数は仮想にすることはできません。仮想概念は実行時にvptrの助けを借りて解決され、vptrはクラスの非静的メンバーであるためです。静的メンバー関数はvptrにアクセスできないため、静的メンバーは仮想ではありません。


2
インスタンス固有の仮想メソッドのみがインスタンスのvtableを必要とします。クラスごとに1つの静的なvtableを使用できます。インスタンスに認識させたい場合は、インスタンスのvtableからクラスstatics vtableをポイントするだけです。
アインポクルム2016

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%である可能性実装(実装されていないだけです)、役立つものだと私は主張します。

通常の仮想関数がどのように機能するかを検討します。staticsを削除し、他のものを追加すると、次のようになります。

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と呼ばれる2つのテーブルを作成し、このような仮想関数にインデックスを割り当てます。

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

まず、OPが要求しているものは矛盾しているという返答は正しいです。仮想メソッドはインスタンスのランタイムタイプに依存します。静的関数は、具体的にはインスタンスに依存せず、型に依存します。つまり、静的関数が型に固有の何かを返すようにするのは理にかなっています。たとえば、Stateパターン用のMouseToolクラスのファミリーがあり、それぞれに静的関数があり、それに付随するキーボード修飾子を返すようにしました。正しい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;
}

ノート:

  • 誠実さはエクササイズとして残されました。
  • 静的から参照を返しました。シングルトンがない限り、それは疑問です。

訪問メソッドの1つが誤った静的関数を呼び出すというコピーアンドペーストエラーを回避したい場合は、テンプレート化されたヘルパー関数(それ自体は仮想関数ではない)を次のようなテンプレートで訪問者に使用できます。

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 2014

それが言葉で矛盾しているかどうかは、意味論の問題です。C ++がインスタンスからstaticを呼び出すことを許可することを想像できます(たとえばの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のように、それ自体からテンプレート化されたクラスがあります。この時点から、myancestorクラスは静的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 ++静的メンバーで示しました。これは、nullポインターを指定して静的メソッドを呼び出すことができることを示しています。

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
技術的には、これは未定義の動作です。何らかの理由でnullポインタを参照することはできません。nullポインターで実行できる唯一のことは、a)別のポインターをそれに割り当て、b)別のポインターと比較することです。
KeithB 2009年

1
さらに、あなただけのそれを比較することができ平等のために。すなわち、別のポインタで(またはinequality_注文ではないp < nullp >= nullなど全てにも不定です。
パベルMinaev

1
@KeithB-完全を期すために、nullポインタに対してdeleteを安全に呼び出すこともできます。
スティーブロウ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.