I have a templated class that I want to avoid copying (because of the potential cost to do so). I can implement a move constructor, but I would also like to allow moving "accross template parameter". Here is what I'm trying to compile:
template <class T>
class Foo
{
public:
Foo() {}
template <class U> Foo(Foo<U>&&) {}
private:
Foo(const Foo&);
};
Foo<int> f() { Foo<float> y; return move(y); }
Foo<int> g() { Foo<int> x; return x; }
Foo<int> h() { Foo<float> z; return z; }
I understand why technically f compiles: the type of move(y) is Foo(float)&& and there happens to be a handy constructor that takes a Foo(U)&&, so the compiler manages to find that U=float works.
h doesn't compile. z is of type Foo(float) and I guess that's too far from Foo(U)&& to figure out that the move constructor can be invoked if U=float is chosen...
I'm not sure why g compiles, but it does. The type of x is Foo(int). How does the compiler manage to use the move operator (it can't just implicit cast from Foo(int) to Foo(int)&&, can it?)
So my questions are: what are the rules? why does h compiles but g doesnt? is there something I can change in Foo to make h compile?
Thank you
A copy or move constructor mustn't be a template. From 12.8(2, 3):
So your example
f
andg
work because you're invoking an ordinary constructor (not a move-constructor).f
works for obvious reasons, because the result ofmove(y)
can bind toFoo<float>&&
.g
works for a different reason: Since the type ofx
is the same as the return type of the function, the value of the expressionx
in the return statement matchesFoo<int>&&
. This is because of 12.8(31, 32):Finally we see why
h
doesn't work: The value of the expressionz
in thereturn
statement cannot bind toFoo<float>&&
, because it is not explicitly cast (viastd::move
), nor given the special dispensation of clause 12.8(32), since its type is not the same as the function's return type. (It can only bind toFoo<float>&
(which would almost surely be wrong) or toFoo<float> const &
.)By the way, there's no point moving objects that don't manage external resources (e.g. primitives). The actual object data has to be copied anyway.