20

What is the correct way to forward all of the parent's constructors in C++0x?

I have been doing this:

class X: public Super {
    template<typename... Args>
        X(Args&&... args): Super(args...) {}
};
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
Neil G
  • 32,138
  • 39
  • 156
  • 257
  • Is there some reason against typing all the constructors rather than using a template short-cut? – Thomas Matthews Jun 25 '10 at 17:25
  • 14
    Yes, time, and if the Super class is updated, then he'll be in a mess of maintenance. The automated solution is infinitely superior. – Puppy Jun 25 '10 at 17:31
  • 1
    @DeadMG - Not really. So what if the base gets a new constructor? The derived doesn't need to use it unless the new information is required. At which point this automated "solution" simply automatically pushes dealing with this to all clients of derived. More often than not this is completely the wrong thing to do. Derived should not be updated unless there's some object that needs it and it should be done so explicitly so that it's obvious how to build the object by looking at its constructor. Reserve variadic templates for variadic conditions. – Edward Strange Jun 25 '10 at 17:42
  • @Thomas - I've found uses for something similar so I imagine there are slightly different problems that are best solved by such a construct. In my case I wanted a generic 'holder' class that would contain any type even if it was not default constructable. Thus the holder itself must accept the same arguments as what it's holding, which can really only be solved in this manner. – Edward Strange Jun 25 '10 at 17:44
  • 1
    @Noah: Calling a constructor of X is a variadic condition- you want to construct X with any constructor of Super, since X doesn't want to know. If base gets a new constructor, Derived can use it automatically instead of having to go back and change all his code manually for the new Super. – Puppy Jun 25 '10 at 18:11
  • "Calling a constructor of X is a variadic condition- you want to construct X with any constructor of Super, since X doesn't want to know." I just explained why that's not true so I don't get the point of your reply. All you've done is repeat yourself. – Edward Strange Jun 25 '10 at 18:27

2 Answers2

46

There is a better way in C++0x for this

class X: public Super {
  using Super::Super;
};

If you declare a perfect-forwarding template, your type will behave badly in overload resolution. Imagine your base class is convertible from int and there exist two functions to print out classes

class Base {
public:
  Base(int n);
};

class Specific: public Base {
public:
  template<typename... Args>
    Specific(Args&&... args);
};

void printOut(Specific const& b);
void printOut(std::string const& s);

You call it with

printOut("hello");

What will be called? It's ambiguous, because Specific can convert any argument, including character arrays. It does so without regard of existing base class constructors. Inheriting constructors with using declarations only declare the constructors that are needed to make this work.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • IMHO - this is the answer that should have been accepted even though the other directly answers the question asked. – Edward Strange Jun 25 '10 at 19:05
  • Thanks Johannes. @Noah Roberts: Yes. – Neil G Jun 25 '10 at 19:21
  • 1
    I am just wondering if the `explicit` keyword would not make the call unambiguous while at the same time allow the perfect forwarding template to work. Not that I would recommend automagically generating derived constructors... – David Rodríguez - dribeas Jun 25 '10 at 22:03
  • 1
    Could this ambiguity be avoided adding a default extra argument for the constructor (ok, maybe forget the *variadic* forwarding :P) with a disable_if condition based on the constructability of B with the deduced arguments? – StephQ Jan 12 '11 at 17:53
  • @Krao that's a work-around, but isn't really a solution. See http://stackoverflow.com/questions/4129023/how-useful-would-inheriting-constructors-be-in-c – Johannes Schaub - litb Jan 12 '11 at 23:33
  • 1
    What is the name of this `using Super::Super` solution? It doesn't seem to be in GCC 4.5. – user2023370 Apr 12 '11 at 10:00
  • 6
    @user643722: It's called "inheriting constructors" and nobody implements it. Not GCC 4.7, not Clang even in trunk, not VC11, *nobody*. – Nicol Bolas Mar 17 '12 at 07:53
  • 2
    As a note, this functionality is now finally available in gcc 4.8. – Jason R Jan 02 '14 at 20:48
6

I think you need to do Super(std::forward<Args>(args)...) if you want things forwarded properly. args has a name, so I believe it will bind to regular references before rvalue references.

More importantly, though, you won't be forwarding copy constructors this way. The compiler will still generate one for you, because it doesn't consider template constructors. In the example provided, you're okay, because the compiler generated one will just call the parent copy constructor anyhow, which is exactly what you want. If that's not appropriate in a more complicated case, then you'll have to write it yourself.

Dennis Zickefoose
  • 10,791
  • 3
  • 29
  • 38
  • 1
    `Super(std::forward(args)...)` is correct, so you were pretty close. Remember, forward requires explicit template argument. – Puppy Jun 25 '10 at 17:30
  • I had that originally, then I second guessed myself. Thanks for the correction. – Dennis Zickefoose Jun 25 '10 at 17:32
  • This is great. Do you have an intuition on what std::forward does? (Is it first-order?) Why can't we just write "args..."? Would that result in copy constructors being called? – Neil G Jun 25 '10 at 17:54
  • 3
    Because if any of them are rvalue references (and the constructor you're calling expects such) then attempting to pass them on will cause problems because they're now lvalue references (they have names). – Edward Strange Jun 25 '10 at 17:56