In the following example the function Table::get_subtable()
is returning a const reference to an internal subobject (SubTable
). This value is then std::move
d and passed into a static factory function from_subtable
that takes the subtable by value. I'm honestly very surprised that this worked and even moved the value (check output)!
#include <iostream>
struct SubTable {
SubTable() = default;
SubTable(SubTable&& other) {
std::cout << "Moved subtable" << std::endl;
}
SubTable(const SubTable&) = default;
static auto from_subtable(SubTable subtable) -> SubTable;
};
auto SubTable::from_subtable(SubTable subtable) -> SubTable
{
return subtable;
}
struct Table {
SubTable _subtable;
auto get_subtable() const -> const SubTable&;
};
auto Table::get_subtable() const -> const SubTable& {
return _subtable;
}
int main()
{
Table hello;
auto subtable = SubTable::from_subtable(std::move(hello.get_subtable()));
}
Output:
Moved subtable
I would have assumed that the move constructor couldn't construct objects from const&&
(which I suppose is what I passed to from_subtable
after the std::move
of the return value). My intuition was that this should result in a compiler error since I can't alter a const reference. Where was I mislead?
you are misinterpreting where the move is happening.
auto SubTable::from_subtable(SubTable subtable) -> SubTable
{
return subtable;
}
auto subtable = SubTable::from_subtable(std::move(hello.get_subtable()));
std::move(hello.get_subtable())
produces a const&&
which is used to copy construct the subtable
argument.
Then, as function arguments cannot be RVOed see why here, the subtable
argument is moved into the return slot. (the return slot is the subtable
variable in main
)
If you delete the copy constructor then the code won't compile.
godbolt demo showing the copy then the move
const&
can bind to all of &
, &&
and const&&
arguments, which is why the code can compile with only a copy constructor but will fail to compile with only a move constructor.