2

From my reading of answers on SO and the cppreference link

The inherited constructors are equivalent to user-defined constructors with an empty body and with a member initializer list consisting of a single nested-name-specifier, which forwards all of its arguments to the base class constructor.

I had concluded that the below classes D and E should behave indentically.

#include <string>
#include <utility>
using namespace std;

class B
{
public:
  B(string&& a) : a(move(a))
  {
  }

  string a;
};

class D : public B
{
public:
  using B::B;
};

class E : public B
{
public:
  E(string&& a) : B(a)
  {
  }
};

string foo()
{
  return "bar";
}

int main()
{
  D d = foo();//This compiles
  E e = foo();//This does not compile
  return 0;
}

E e = foo() rightly fails to compile, since B's constructor only accepts a string&&. However, D d = foo() goes through fine. Why is that? The compiler used is clang3.5.

EDIT: Also, as explained in this answer, the perfect forwarding idiom isn't a replacement for inheriting constructors. So, what does the body look like exactly?

Community
  • 1
  • 1
Pradhan
  • 16,391
  • 3
  • 44
  • 59

2 Answers2

2

However, D d = foo() goes through fine. Why is that?

Because using B::B effectively passes the temporary string straight to B's constructor, where it can still be bound ala && (i.e. its value category is still xvalue), then does any extra derived-class initialisation (if there were other data members, a VDT etc). That's highly desirable as the point of using the base class constructors is to allow the same client usage.

(That contrasts with E(string&&), inside which where the named a parameter is no longer considered a xvalue (expiring temporary) ripe for passing to B::B.)

(If you haven't already, you might want to have a look at (std::forward)[http://en.cppreference.com/w/cpp/utility/forward] too... it helps with perfect forwarding of arguments)

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252
  • What would the definition look like though? The question just had one example. But what if, say, D had other members with default constructors? When the cppreference link says forward, is it actually implying usage of the universal reference + `std::forward` idiom? If so, how would it get around the issues outlined [here](http://stackoverflow.com/a/4129077/862351)? – Pradhan Jan 15 '15 at 06:52
  • 1
    *"What would the definition look like though?"* - it wouldn't... any mental approximation you make is flawed, as explained in "the issues outlined here" link you've just posted where it mentions the issue of explicitness of constructors. Instead of trying to mentally model the derived constructor as C++ code calling the base class destructor, I suggest you think of the exposed base class constructor as being made accessible through the derived class interface with some potential hidden pre- and/or post- steps for initialising other bases and members, the VDT etc.. – Tony Delroy Jan 15 '15 at 07:13
2

The wording from the standard about what the inherited constructor definition in the derived class looks like is a bit more explicit than what the cppreference description alludes to, but the key phrase in the latter is forwards all of its arguments. In other words, the value category of the arguments is preserved, which your definition of E doesn't do, and consequently fails to compile.

From N3337, §12.9/8 [class.inhctor]

... An implicitly-defined inheriting constructor performs the set of initializations of the class that would be performed by a user-written inline constructor for that class with a mem-initializer-list whose only mem-initializer has a mem-initializer-id that names the base class denoted in the nested-name-specifier of the using-declaration and an expression-list as specified below, and where the compound-statement in its function body is empty (12.6.2). If that user-written constructor would be ill-formed, the program is ill-formed. Each expression in the expression-list is of the form static_cast<T&&>(p), where p is the name of the corresponding constructor parameter and T is the declared type of p.

So the constructor arguments are being perfectly forwarded to the corresponding inherited constructor (std::forward (§20.2.3) is specified to return static_cast<T&&>(p), exactly the same as the description above). Depending on the declared type of the constructor parameter, reference collapsing occurs as described in this answer.

In your case, [T=string&&] and the cast yields string&& again, which can bind to B's parameter. To match that behavior in E you should rewrite the constructor as

E(string&& a) : B(static_cast<string&&>(a))
{
}
Community
  • 1
  • 1
Praetorian
  • 106,671
  • 19
  • 240
  • 328
  • Sorry if I am missing something obvious, but the emphasized part of the standard quote sounds identical to the approach that the [link](http://stackoverflow.com/questions/4129023/how-useful-would-inheriting-constructors-be-in-c/4129077#4129077) in the question deems insufficient. – Pradhan Jan 15 '15 at 08:53
  • 1
    @Pradhan It is insufficient, if you follow that description and implement the forwarding constructor, you will need to mark it `explicit` manually, unlike the using declaration which inherits the explicitness, if any, of the base constructor. The standard does not address that when it compares the behavior of the inherited constructor to that of a user provided one. – Praetorian Jan 15 '15 at 09:12