How useful is it to inherit constructors in C ++?

As I sit at meetings of the C ++ Standards Committee, they discuss the pros and cons of dropping Inheriting Constructors , since the compiler provider did not implement it (a meaning that users did not ask for it).

Let me quickly remind everyone who inherits constructors:

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

Some vendors suggest that with r-value references and variational patterns (ideal redirect constructors) it would be trivial to provide a forwarding constructor in an inheriting class that could avoid the inheritance of constructors.

For example,

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

I have two questions:

1) Can you provide examples from the real world (not far-fetched) from your programming experience that would greatly benefit from the inheritance of constructors?

2) Are there any technical reasons that you might think about to eliminate the possibility of “ideal forwarding constructors” as an adequate alternative?

Thank!

+31
c ++ inheritance constructor c ++ 11 perfect-forwarding
Nov 08 2018-10-11T00:
source share
5 answers

2) Are there any technical reasons that you might think about to eliminate the possibility of “ideal forwarding constructors” as an adequate alternative?

I showed one problem with this perfect transition: Forwarding all the constructors in C ++ 0x .

In addition, the ideal redirection method cannot “force” the clarity of the constructors of the base class: either it is always the constructor of the transformation, or never, and the base class will always have direct initialization (always using all the constructors, even explicit ones).

Another problem is list-initializer-constructors, because you cannot output Args to initializer_list<U> . Instead, you will need to forward the database using B{args...} (note the curly braces) and initialize the D objects with (a, b, c) or {1, 2, 3} or = {1, 2, 3} . In this case, Args will be the type of the elements of the initializer list and redirects them to the base class. After that, the constructor-initializer can get them. This seems to cause unnecessary code bloat because the template argument package potentially contains many type sequences for each different combination of types and lengths, and since you need to choose the initialization syntax, this means:

 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] (!!) 
+21
Nov 08 '10 at 23:19
source share

The disadvantages of the pair to the proposed workaround:

  • Longer
  • He received more tokens.
  • It uses completely new complex language features.

In general, the cognitive complexity of the workaround is very poor. Much worse than, for example, the default special member functions for which simple syntax has been added.

Real-world motivation for constructor inheritance: AOP combinations are implemented using re-inheritance instead of multiple inheritance.

+4
Nov 08 '10 at 23:26
source share

In addition to what others have said, consider this artificial example:

 #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 AT; }; template< class A, class B > struct Choose_< sizeof( NoType ), A, B > { typedef BT; }; 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 >::TT; }; int main() { using namespace std; cout << MyWrapper< int >::T::name() << endl; cout << MyWrapper< char const* >::T::name() << endl; } 

At least with MinGW g ++ 4.4.1, compilation failed due to the forwarding of the C ++ 0x constructor.

Does it compile fine with "manual" forwarding (commented out constructors) and presumably / possibly also with inherited constructors?

Greetings and hth.

+3
Nov 09 2018-10-11T00: 00Z
source share

I see a problem when a new class has member variables that need to be initialized in the constructor. This will be a common case, since usually a derived class will add some state to the base class.

I.e:

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

For those who are trying to solve it: how do you distinguish between :B(a), m(b) and :B(b), m(a) ? How do you handle multiple inheritance? virtual inheritance?

If only the simplest case is solved, in practice it will have very limited utility. Unsurprisingly, compiler providers have not yet implemented the proposal.

0
Nov 08 '10 at 23:52
source share

Philosophically, I am against the inheritance of designers. If you define a new class, you determine how it will be created. If most of this construction can take place in the base class, then it is quite reasonable for you to redirect this work to the base class constructor in the initialization list. But you still need to explicitly do this.

-one
Nov 08 '10 at 23:25
source share



All Articles