c++11inheritanceconstructorlanguage-lawyerusing

Is this a standard conforming way for selective constructor "inheritance"?


In trying to inherit constructors selectively (ie just some of them) I found a way that seem to work with GCC:

 #include <string>
 #include <iostream>

 struct B
 {
     B()
     {
         std::cout << std::string("B::B() called") << std::endl;
     }

     B(int)
     {
         std::cout << std::string("B::B(int) called") << std::endl;
     }

     B(long)
     {
         std::cout << std::string("B::B(long) called") << std::endl;
     }


     B(long, long)
     {
         std::cout << std::string("B::B(long, long) called") << std::endl;
     }
 };


 struct D : public B
 {
     D(int) = delete;
     D(long, long)
     {
         std::cout << std::string("D::D(long, long) called") << std::endl;
     }

     using B::B;
 };



 #pragma GCC diagnostic push // Ignore warnings from unused variables
 #pragma GCC diagnostic ignored "-Wunused-variable"
 int main()
 {
     B b0;
     B b1(1);
     B b2(1, 2);
     D d0;
     // D d1(1);   error despite we might wish B::B(long) to be selected
     D d2(1, 2);
 }
 #pragma GCC diagnostic pop

ie those I don't want that using? brings down I just delete declare or define my own constructor in the derived class and both seem to stomp out the inherited overload.

Is this standard conforming? I also noted that it doesn't seem to matter whether the using declaration is first or last. Is that too standard conforming? It isn't entirely the same as selective inheritance as the deleted overload would still attract attention when resolving overloads (as the out commented line indicates).


Solution

  • This is the expected behaviour, indeed since D(int) is explicitly deleted, it will be selected when calling with an int rather than an implicit conversion to another overload.

    If you wanted D's int constructor to call B's long constructor you could just forward the call.

    D(int x) : B((long)x) {}
    

    This will behave as if you'd write

    D d1(1l);
    

    But it begs the question of why we're doing this, if the types should always be implicitly casted then why have all those implementations in the first place.