代码之家  ›  专栏  ›  技术社区  ›  Faisal Vali

在C++中继承构造函数有多大的用处?

  •  31
  • Faisal Vali  · 技术社区  · 14 年前

    Inheriting Constructors 因为还没有编译器供应商实现它(用户没有要求它)。

    让我快速提醒大家什么是继承构造函数:

    struct B
    {
       B(int);
    };
    
    struct D : B
    {
      using B::B;
    };
    

    一些供应商建议,使用r值引用和可变模板(完美的转发构造函数),在继承类中提供一个转发构造函数将很容易避免继承构造函数。

    例如:

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

    1) 您能否从编程经验中提供实际的(非人为的)示例,这些示例将从继承构造函数中受益匪浅?

    2) 有没有什么技术上的原因会妨碍“完美的转发构造函数”成为合适的替代方案?

    谢谢!

    5 回复  |  直到 13 年前
        1
  •  21
  •   Community CDub    7 年前

    2) 有没有什么技术上的原因会妨碍“完美的转发构造函数”成为合适的替代方案?

    我在这里展示了完美转发方法的一个问题: Forwarding all constructors in C++0x

    而且,完美的转发方法不能“转发”基类构造函数的明确性:要么它总是一个转换构造函数,要么永远不会,基类将始终直接初始化(总是使用所有构造函数,甚至显式构造函数)。

    另一个问题是初始化列表构造函数,因为您无法推断 Args initializer_list<U> . 相反,你需要用 B{args...} (注意大括号)并初始化 D 对象具有 (a, b, c) {1, 2, 3} = {1, 2, 3} . 在这种情况下, 参数

    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] (!!)
    
        2
  •  4
  •   Ben Voigt    14 年前

    拟议解决方案的几个缺点:

    • 时间更长了
    • 有更多的代币
    • 它使用了全新的复杂语言特征

    总的来说,工作的认知复杂性是非常糟糕的。比默认的特殊成员函数更糟糕,因为它添加了一个简单的语法。

    构造函数继承的实际动机:AOP混合使用重复继承而不是多重继承实现。

        3
  •  3
  •   Cheers and hth. - Alf    14 年前

    除了其他人所说的以外,考虑一下这个人为的例子:

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

    至少使用MIWG G+4.4.1,编译失败是由于C++ 0x构造函数转发。

    干杯。

        4
  •  0
  •   Sjoerd    14 年前

    即:

    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) ? 如何处理多重继承?虚拟继承?

        5
  •  -1
  •   miked    14 年前

    哲学上,我反对继承构造函数。如果要定义一个新类,则要定义如何创建它。如果大多数构造都可以在基类中进行,那么将该工作转发给初始化列表中基类的构造函数是完全合理的。但你仍然需要明确地去做。