2

おそらくさらに良いのは、これらの状況で標準が基本クラスへの転送を要求するのはなぜですか?(ええええええ-なぜですか?-なぜなら)

class B1 {
public:
    virtual void f()=0;
};
class B2 {
public:
    virtual void f(){}
};
class D : public B1,public B2{
};
class D2 : public B1,public B2{
public:
    using B2::f;
};
class D3 : public B1,public B2{
public:
    void f(){
        B2::f();
    }
};
D d;
D2 d2;
D3 d3;

MSは以下を提供します:

sourceFile.cpp
sourceFile.cpp(24) : error C2259: 'D' : cannot instantiate abstract class
        due to following members:
        'void B1::f(void)' : is abstract
        sourceFile.cpp(6) : see declaration of 'B1::f'
sourceFile.cpp(25) : error C2259: 'D2' : cannot instantiate abstract class
        due to following members:
        'void B1::f(void)' : is abstract
        sourceFile.cpp(6) : see declaration of 'B

MSコンパイラについても同様です。

私は最初のケースを買うかもしれません、D。しかし、D2-fはusing宣言によって明確に定義されていますが、コンパイラがvtableに入力する必要があるのになぜそれだけでは不十分なのですか?

この状況は標準のどこで定義されていますか?

回答に応じて追加

私が受け入れた以下の答えについて:

なぜこれが仕様のエラーに見えないのですか?-一連の非仮想f()を持つ継承階層があり、派生クラスでの使用がステートメントを使用して決定され、基本クラスのfのdeclが仮想に変更される場合、これにより、 fは、fを選択するためのステートメントを使用して派生クラスで呼び出されます。それは私が気づかなかったc++の「落とし穴」です。それは言語の一部かもしれませんが、そのような「遠隔作用」は私を不安にさせ、私にはある種の正確性/維持の原則に違反しているように見えます(私は今は完全に定式化できません)。

しかし、私は例をあげることができます:

#include <iostream>
using std::cout;


namespace NonVirtual_f{

class C0 {
public:
    void f(){cout<<"C0::f()"<<'\n';}
};

class C1 : public C0{
public:
    void f(){cout<<"C1::f()"<<'\n';}
};

class C2 : public virtual C1{
public:
    void f(){cout<<"C2::f()"<<'\n';}
};

class D3 : public virtual C1, public C2{
public:
    using C1::f;
};


}//namespace NonVirtual_f

namespace Virtual_f{


class C0 {
public:
    virtual void f(){cout<<"C0::f()"<<'\n';}
};

class C1 : public C0{
public:
    void f(){cout<<"C1::f()"<<'\n';}
};

class C2 : public virtual C1{
public:
    void f(){cout<<"C2::f()"<<'\n';}
};

class D3 : public virtual C1, public C2{
public:
    using C1::f;
};



}//namespace Virtual_f




int main(int argc,const char* const*argv){

    NonVirtual_f::D3 nv3;
    nv3.f();

    Virtual_f::D3 v3;
    v3.f();

    return 0;    
} 

出力:

C1::f()
C2::f()

変更されるのは、C0のfの仮想性だけです。特に、基本クラスのfの非仮想性が選択されると、すぐ上の例のように、派生クラス(一般に知ることができない)が「オーバーライド」された場合、メンテナンスの問題なしに変更することはできません。

「まあ、NonVirtualの場合はそのようにオーバーライドしないでください」と反論する場合、それは悪い習慣であることに同意しますが、これはそれだけではないようです。私にとって、言語は次のようにすべきです。

NonVirtual :: D3での使用を許可しない([関数の場合に許可された署名を使用しない限り]他のオーバーロードされたfが取り込まれる可能性があるため、現在は不可能です)

また

関数のステートメントの使用を完全に禁止し、転送を強制します

また

すべての場合に実際にオーバーライドを使用しています

また

次のような関数(基本的には関数を使用)の構文宣言を許可します。

void f(*signature*) = C2::f;

正確には、私はここで何が欠けていますか?誰かが標準でこの選択の「理由」を明確にするシナリオを思い付くことができますか?

4

1 に答える 1

3

C++標準は§10.3/2で次のように述べています。

メンバールックアップのルール(10.2)は、派生クラスのスコープ内の仮想関数の最終的なオーバーライドを決定するために使用されますが、 using-declarationsによって導入された名前は無視されます。

したがって、を使用using B2::f;B2::f()て派生クラスに取り込む場合でも、をオーバーライドするとは見なされませんB1::f()

したがって、D2§10.4/4のために抽象的です。

クラスは、最終的なオーバーライドが純粋仮想である純粋仮想関数を少なくとも1つ含むか継承する場合、抽象です。

于 2010-05-29T02:32:43.377 に答える