The output of the following program...
#include <iostream>
using namespace std;
struct X
{
X(const X&) { cout << "copy" << endl; }
X(X&&) { cout << "move" << endl; }
template<class T> X(T&&) { cout << "tmpl" << endl; }
};
int main()
{
X x1 = 42;
X x2(x1);
}
is
tmpl
tmpl
The desired output is:
tmpl
copy
Why doesn't the concrete copy constructor take precedence over the template constructor?
Is there anyway to fix it so that the copy and move constructor overloads will take precedence over the template constructor?
Move constructor moves the resources in the heap, i.e., unlike copy constructors which copy the data of the existing object and assigning it to the new object move constructor just makes the pointer of the declared object to point to the data of temporary object and nulls out the pointer of the temporary objects.
If a copy constructor, copy-assignment operator, move constructor, move-assignment operator, or destructor is explicitly declared, then: No move constructor is automatically generated. No move-assignment operator is automatically generated.
A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.
The answer is No. The creation of the object memory is done via the new instruction. Copy constructor is then in charge of the actual copying (relevant only when it's not a shallow copy, obviously). You can, if you want, explicitly call a different constructor prior to the copy constructor execution.
Well, it is because of reference-collapsing.
At the overload resolution stage, when the function template is instantiated, T
is deduded to be X&
, so T&&
(which is X& &&
) becomes X&
due to reference-collapsing, and the instantiated function from the function template becomes exact match and the copy-constructor requires a conversion from X&
to const X&
(which is why it is not selected being inferior match).
However, if you remove the const
from the copy-constructor, the copy-constructor will be preferred. Try this:
X(/*const*/ X&) { cout << "copy" << endl; }
Output as expected.
Or alternatively, if you make the parameter in the function template as const T&
then copy-constructor will be called (even if it remains same!), because reference-collapsing will not come into picture now:
template<class T> X(const T &) { cout << "tmpl" << endl; }
Output is expected, again.
If you don't want to add another constructor (as other answers suggested), you can use SFINAE to constrain the call, by replacing your template constructor by this:
template<class T
, typename std::enable_if<not std::is_same<X, typename std::decay<T>::type>::value, int>::type = 0
> X(T&&) { cout << "tmpl " << endl; }
Which just involves adding a dummy
default template argument (a known technique: Link). No extra headers are necessary.
You will get the desired output.
I got this answer from a related problem: Link. All this looks rather inelegant, but seems to be the only way out for now. I still would like to see a more elegant solution.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With