Reputation: 16777
C++11 allows inheriting constructors making it possible to avoid a lot of boilerplate, especially with something like a wrapper class. However, it seems like you could already achieve this functionality with variadic templates alone.
class B
{
public:
B(int){//do something}
B(int, char){//do something}
};
Using inheriting constructors:
class D : public B
{
public:
using B::B;
};
Using variadic templates and forward:
class D : public B
{
public:
template <typename...Args>
D(Args&&... args) : B(std::forward<Args>(args)...)
{
}
};
While consistency(treat constructors and methods the same way for using
) and ease of use are very good reasons to bring inherited constructors into the language, is there any other reason why the first solution should be preferred to the second? Both the CWG documents(N1890 and N1898) I found discussing inheriting constructors simply note the following and move on:
Little more than a historical accident prevents using this to work for a constructor as well as for an ordinary member function. Had a constructor been called “ctor” or “constructor” rather than being referred to by the name of their class, this would have worked. We propose this as the mechanism for inheriting constructors.
Upvotes: 4
Views: 196
Reputation: 145457
Perfect forwarding isn't quite perfect. In particular, 0 as actual argument reduces to int
instead of denoting a general nullvalue. So with a constructor taking a pointer argument, 0
will work as actual argument for an inherited constructor, but not for the forwarding.
Another reason that I think I mentioned at one time when the question (do we really need inheriting constructors) was aired, is that inheriting constructors is a simple notation for a conceptually simple thing, to be used in the context of a simple class. There is enough forced complexity and forced boilerplate in C++.
Upvotes: 3
Reputation:
Maintenance issues (compiler errors) might lead to preference of 'using':
struct Base
{
Base(int) {}
};
struct Derived : public Base
{
using Base::Base;
};
struct DerivedTemplate : Base
{
template <typename...Args>
DerivedTemplate(Args&&... args)
: Base(std::forward<Args>(args)...)
{}
};
int main()
{
// error: no matching function for call to ‘Derived::Derived(int, int)’
Derived d(1, 2);
// In instantiation of ‘DerivedTemplate::DerivedTemplate(Args&& ...) [with Args = {int, int}]’:
// required from here
// error: no matching function for call to ‘Base::Base(int, int)’
DerivedTemplate dt(1, 2);
}
Also, each template instantiation is a different constructor (while using does not multiply)
Upvotes: 2
Reputation: 304122
The big reason is that perfect forwarding isn't perfect. Simple case:
struct B {
B(int* ) { .. }
};
Now consider:
D d{0};
If we inherit the constructor, this works fine. If we perfect-forward, we would try to construct B(int )
, since 0
deduces as int
, which is a constructor that doesn't exist. Failure!
Other failure cases include braced initialization, declaration-only static const data members, overloaded functions, and bitfields.
Upvotes: 4