My understanding about c++ implicit copy constructor is similar to
T(T const& x) :
base1(x), base2(x) ... ,
var1(x.var1), var2(x.var2)...
{}
Move constructor, copy & move assignment also follows similar pattern.
Why was it not defined similar to the following?
T(T const& x) :
base1(static_cast<base1 const&>(x)),
base2(static_cast<base2 const&>(x)) ... ,
var1(x.var1), var2(x.var2)...
{}
Example
I had a class which had implicit copy/move constructor/assignment operator, as well as some converting constructors. I was delegating the job to some implementation class.
class common_work //common implementation of many work like classes
{
common_work(common_work const&) = default;
common_work(common_work&&) = default;// ... implicit constructors work for me.
//a forwarding constructor which can take many work like objects
template<class T, enable_if<work_like<T> > >
common_work(T&& x) { ... }
};
class work1 //one of the implementation
{
work1(work1 const& ) = default;
work1(work1&& ) = default; ...
common_work impl_;
};
This was fine, as work1
copy/move constructors was calling copy/move constructor for common_work
, and forwarding constructor was used by other constructors [not shown in code] which converts from another kind of work
.
Then I thought to inherit work1
from common_work
for EBO and other reasons. So the new work1
class looked like
class work1 : private common_work
{
work1(work1 const& ) = default;
work1(work1&& ) = default; ...
};
But as, work1
is a work_like
class, suddenly the forwarding constructor was getting a better match, as the copy/move constructor for common_work
requires a static_cast
from derived to base.
NOTE :
- There is a similar kind of example given by Scott Meyers, where copy construction triggers forwarding constructor as copy constructor requires a const addition, while forwarding constructor requires none. But I think, that problem arises due to wrong class design, while problem here is due to argument passed to base class during implicit copy/move are not exact match.
- I can not write a universal forwarding constructor/assignment, and delete the implicit ones, because deleted functions also participate in overload resolution and cause error if matched exactly.
- The solution presently I have is to make
common_work
as a CRTP , i.e. derived class type passed as template argument, and in forwarding constructor filter it out as enable_if<and_<work_like<T>,not_<is_same<T,Derived> > > >
. Otherwise I have to manually write copy/move constructor/assignment for work1
and static_cast
to base classes explicitly, which is buggy, error prone, and maintenance hazard.