Reputation: 2734
I am doing some "experiments" with move semantics in order to fully understand it. I have reached a point that I don't understand completely. This is because it seems that there is a conflict with a constructor like A::A(B b)
and A::A(B &&b)
when using std::move
. Below is the code that explains this:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
};
struct Container {
Container(Item i) {
std::cout << "Container::Container(Item )\n";
}
Container(Item &&i) {
std::cout << "Container::Container(Item&& )\n";
}
};
int main() {
Item i;
Container c(std::move(i));
return 0;
}
When trying to compile the above code, the followign error is shown:
error C2668: 'Container::Container' : ambiguous call to overloaded function
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(21): could be 'Container::Container(Item &&)'
1> c:\users\xe74139\documents\visual studio 2012\projects\project1\project1\maincpp.cpp(17): or 'Container::Container(Item)'
I know that the constructor Container(Item i)
makes no sense at all, but I still don't understand why C++ compiler sees a conflict between both Container
constructors.
Why it can't determine that Container c(i)
is for calling Container(Item)
and Container c(std::move(i))
is for calling Container(Item &&)
?
EDIT: Or, in other words, why the constructor Container(Item)
is valid for a call like Container c(std::move(i))
?
Upvotes: 2
Views: 132
Reputation: 131656
Why the constructor Container(Item) is valid for a call like Container c(std::move(i))?
Because you can construct an Item
when given the result of std::move(i)
(which is an Item&&
).
And like HowardHinnant said in a comment - it's the same for Item
vs Item&
- you can construct one when passed the other, so if you have both of these constructors defined you've created amibguity.
Upvotes: 1
Reputation: 25895
As far as I can tell, it's because the argument first evaluated, then passed. Once it's passed, it's just any r-value
, and both your constructors would work. I'm not sure it's possible to have such an overload, and it doesn't make sense from my point of view.
Why not? Well, you are trying to define a container that forces movement of an element. From a design view, you have to consider that someone using that code may not want the data "stolen" (moved). I think it would make more sense in this context to have:
struct Item {
Item() { }
Item(const Item &other) {
std::cout << "Item::Item(const Item& )\n";
}
Item(Item &&other) {
std::cout << "Item::Item(Item&&)\n";
}
};
This overload makes more sense - you will explicitly state when you want the object moved, and when copied (there's a default move
here, but this way makes it more obvious). Now you just need
Container(Item i);
(without the other constructor) and calling it with
Container c(std::move(i));
is what we expect - instead of copying i
, you're explicitly stating you want it moved, and i
is in charge of handling this.
Upvotes: 2