31

私が C++ 標準委員会の会議に出席しているとき、彼らは継承コンストラクターを削除することの長所と短所について議論しています。これは、コンパイラ ベンダーがまだそれを実装していないためです (ユーザーが要求していないという意味です)。

継承コンストラクターとは何かを簡単に思い出させてください。

struct B
{
   B(int);
};

struct D : B
{
  using B::B;
};

一部のベンダーは、右辺値参照と可変個引数テンプレート (完全な転送コンストラクター) を使用して、継承コンストラクターを不要にする継承クラスで転送コンストラクターを提供することは簡単であると提案しています。

例:

struct D : B
{
  template<class ... Args> 
    D(Args&& ... args) : B(args...) { } 
};

2 つの質問があります。

1) コンストラクターを継承することで大きなメリットが得られるプログラミング経験から、実際の (不自然な) 例を提供できますか?

2)「完全な転送コンストラクター」が適切な代替手段になることを妨げる、考えられる技術的な理由はありますか?

ありがとう!

4

5 に答える 5

21

2)「完全な転送コンストラクター」が適切な代替手段になることを妨げる、考えられる技術的な理由はありますか?

ここで、その完全な転送アプローチに関する 1 つの問題を示しました。C++0x ですべてのコンストラクターを転送することです。

また、完全な転送アプローチでは、基本クラスのコンストラクターの明示性を「転送」することはできません。それは常に変換コンストラクターであるか、決してそうではなく、基本クラスは常に直接初期化されます (常にすべてのコンストラクターを使用し、明示的なコンストラクターを使用します)。もの)。

もう 1 つの問題は、イニシャライザ リスト コンストラクタであると推測できませArgsinitializer_list<U>。代わりに、B{args...}(中括弧に注意してください) を使用してベースに転送し、またはまたはを使用Dしてオブジェクトを初期化する必要があります。その場合、初期化子リストの要素の型になり、それらを基本クラスに転送します。その後、イニシャライザ リスト コンストラクタがそれらを受け取ることができます。テンプレート引数パックには、型と長さのさまざまな組み合わせごとに多くの型シーケンスが含まれる可能性があり、初期化構文を選択する必要があるため、これは不要なコードの膨張を引き起こすようです:(a, b, c){1, 2, 3}= {1, 2, 3}Args

struct MyList {
  // initializes by initializer list
  MyList(std::initializer_list<Data> list);

  // initializes with size copies of def
  MyList(std::size_t size, Data def = Data());
};

MyList m{3, 1}; // data: [3, 1]
MyList m(3, 1); // data: [1, 1, 1]

// either you use { args ... } and support initializer lists or
// you use (args...) and won't
struct MyDerivedList : MyList {
  template<class ... Args> 
  MyDerivedList(Args&& ... args) : MyList{ args... } { } 
};

MyDerivedList m{3, 1}; // data: [3, 1]
MyDerivedList m(3, 1); // data: [3, 1] (!!)
于 2010-11-08T23:19:30.430 に答える
4

提案された回避策にはいくつかの欠点があります。

  • 長いです
  • それはより多くのトークンを持っています
  • それは真新しい複雑な言語機能を使用しています

全体として、回避策の認知の複雑さは非常に悪いです。たとえば、単純な構文が追加されたデフォルトの特別なメンバー関数よりもはるかに悪いです。

コンストラクター継承の実際の動機: 多重継承ではなく反復継承を使用して実装された AOP ミックスイン。

于 2010-11-08T23:26:32.303 に答える
3

他の人が言ったことに加えて、次の人工的な例を検討してください。

#include <iostream>

class MyString
{
public:
    MyString( char const* ) {}
    static char const* name() { return "MyString"; }
};

class MyNumber
{
public:
    MyNumber( double ) {}
    static char const* name() { return "MyNumber"; }
};

class MyStringX: public MyString
{
public:
    //MyStringX( char const* s ): MyString( s ) {}              // OK
    template< class ... Args > 
        MyStringX( Args&& ... args ): MyString( args... ) {}    // !Nope.
    static char const* name() { return "MyStringX"; }
};

class MyNumberX: public MyNumber
{
public:
    //MyNumberX( double v ): MyNumber( v ) {}                   // OK
    template< class ... Args > 
        MyNumberX( Args&& ... args ): MyNumber( args... ) {}    // !Nope.
    static char const* name() { return "MyNumberX"; }
};

typedef char    YesType;
struct NoType { char x[2]; };
template< int size, class A, class B >
struct Choose_{ typedef A T; };
template< class A, class B >
struct Choose_< sizeof( NoType ), A, B > { typedef B T; };

template< class Type >
class MyWrapper
{
private:
    static Type const& dummy();
    static YesType accept( MyStringX );
    static NoType accept( MyNumberX );
public:
    typedef typename
        Choose_< sizeof( accept( dummy() ) ), MyStringX, MyNumberX >::T T;
};

int main()
{
    using namespace std;
    cout << MyWrapper< int >::T::name() << endl;
    cout << MyWrapper< char const* >::T::name() << endl;
}

少なくとも MinGW g++ 4.4.1 では、C++0x コンストラクターの転送が原因でコンパイルが失敗します。

「手動」転送(コメントアウトされたコンストラクター)で正常にコンパイルされ、おそらく/おそらく継承されたコンストラクターでもコンパイルされますか?

乾杯 & hth。

于 2010-11-09T00:28:08.103 に答える
0

新しいクラスに、コンストラクターで初期化する必要があるメンバー変数がある場合に問題が発生します。通常、派生クラスは何らかの状態を基本クラスに追加するため、これは一般的なケースです。

あれは:

struct B 
{ 
   B(int); 
}; 

struct D : B 
{ 
   D(int a, int b) : B(a), m(b) {}
   int m;
}; 

それを解決しようとしている人のために: どうやって と を区別します:B(a), m(b):B(b), m(a)? 多重継承をどのように処理しますか? 仮想継承?

最も単純なケースだけが解決された場合、実際の有用性は非常に限られます。コンパイラ ベンダーがまだ提案を実装していないのも不思議ではありません。

于 2010-11-08T23:52:40.540 に答える
-1

哲学的に、私はコンストラクターの継承に反対です。新しいクラスを定義している場合は、その作成方法を定義しています。その構築の大部分が基本クラスで行われる場合、その作業を初期化リストの基本クラスのコンストラクターに転送することは完全に合理的です。ただし、明示的に行う必要があります。

于 2010-11-08T23:25:22.767 に答える