Why can we use `std::move` on a `const` object?
struct strange {
mutable size_t count = 0;
strange( strange const&& o ):count(o.count) { o.count = 0; }
};
const strange s;
strange s2 = std::move(s);
here we see a use of std::move
on a T const
. It returns a T const&&
. We have a move constructor for strange
that takes exactly this type.
And it is called.
Now, it is true that this strange type is more rare than the bugs your proposal would fix.
But, on the other hand, the existing std::move
works better in generic code, where you don't know if the type you are working with is a T
or a T const
.
Why does calling std::move on a const object call the copy constructor when passed to another object?
The type of the result of calling std::move
with a T const
argument is T const&&
, which cannot bind to a T&&
parameter. The next best match is your copy constructor, which is deleted, hence the error.
Explicitly delete
ing a function doesn't mean it is not available for overload resolution, but that if it is indeed the most viable candidate selected by overload resolution, then it's a compiler error.
The result makes sense because a move construction is an operation that steals resources from the source object, thus mutating it, so you shouldn't be able to do that to a const
object simply by calling std::move
.
Is there any way to correctly std::move const data?
There is no way to std::move
a const T
as it would mutate the given object. (Unless you make the members mutable
and make a T(const T&&)
constructor to do the move, which I would not recommend).
A better solution in my opinion would be to make all the mutating functions private, such that only the builder can access them, and return a T&&
.
why std::move works with constant object
Because std::move
makes moving an object possible. It does not actually move anything. That would be the move constructor of s
.
But since the result of the move here will still be const
it won't call the move constructor. It will call the copy constructor.
Try it. You'll see.
Moving members of class passed as a const reference argument
Moving const objects is generally a simple copy (unless you overload Object(const Object&&)
) so your usage of std::move
seems useless.
If the members of ResourceInserter
are (non-const) references,
your const ResourceInserter&
is "misleading", and your move will actually happen.
Why does std::move copy contents for a rvalue or const lvalue function argument?
Your vector is actually copied, not moved. The reason for this is, although declared as an rvalue reference, vec_
denotes an lvalue expression inside the function body. Thus the copy constructor of std::vector
is invoked, and not the move constructor. The reason for this is, that vec_
is now a named value, and rvalues cannot have names, so it collapses to an lvalue. The following code will fail to compile because of this reason:
void foo(int&& i)
{
int&& x = i;
}
In order to fix this issue, you have to make vec_
nameless again, by calling std::move(vec_)
.
Passing the const-qualified object to the 'std::move'
Your code:
std::make_pair<const int, const int>( std::move( MIN_ALLOWED_Y ), std::move( MAX_ALLOWED_Y ) )
Is overly complicated. Not only are the move
s pointless as PVS Studio told you, but using make_pair
when explicitly specifying the types is pointless. You can simplify to:
std::pair<const int, const int>( MIN_ALLOWED_Y, MAX_ALLOWED_Y )
Which does the same thing without excess ceremony.
what does std::move(const shared_ptr reference) mean?
std::move
is a function which converts the argument to an rvalue reference. The function call is an xvalue expression.
When the argument is a reference to const, then the result of the conversion is an rvalue to const. If you initialise from rvalue to const, copy constructor will be used because the rvalue reference parameter to non-const of the move constructor cannot bind to rvalue reference argument to const.
I think there is also an implicit question by OP of how
_p(std::move(p))
might differ from_p(p)
_p(std::move(p))
doesn't differ from _p(p)
in case of const std::shared_ptr<T>
.
In theory, if decltype(_p)
was a type that had a constructor T(const T&&)
, then there would be a difference, since that constructor would be invoked by _p(std::move(p))
but not by _p(p)
. Such constructor would be quite unconventional, but technically well-formed. std::shared_ptr
doesn't have such constructor.
Why move on const objects work?
You're not moving anything.
std::move
is really poorly named: it doesn't force a move; it just returns an rvalue. It's up to the compiler to decide which constructor of std::vector<int>
to invoke, and that's what determines whether you get a move.
If the container can't be moved because the target's move constructor isn't a match, then the copy constructor will be used instead, through basic overload rules.
#include <iostream>
struct T
{
T() = default;
T(const T&) { std::cout << "copy ctor\n"; }
T(T&&) { std::cout << "move ctor\n"; }
};
int main()
{
T a;
T b = std::move(a); // "move ctor"
const T c;
T d = std::move(c); // "copy ctor" - `const T&&` only matches copy ctor
// (shut up GCC)
(void) b;
(void) d;
}
(live demo)
It's designed this way (const T&&
being able to bind to const T&
) at least in part because moving is intended to be best-effort, exactly so that you don't have to fight with compiler errors in cases like this.
Related Topics
Osx - Replace Gcc Version 4.2.1 with 4.9 Installed via Homebrew
Is a Destructor Called When an Object Goes Out of Scope
Opencv Cv::Mat and Eigen::Matrix
Why Does Clang Optimize Away X * 1.0 But Not X + 0.0
Error: Member Access into Incomplete Type:Forward Declaration Of
C++ How to Generate All the Permutations of Function Overloads
When Do Compilers Inline C++ Code
Can Class Template Constructors Have a Redundant Template Parameter List in C++20
Boost::Flat_Map and Its Performance Compared to Map and Unordered_Map
What Does _Declspec(Dllimport) Really Mean
What Is the Fastest Way to Change a Key of an Element Inside Std::Map
C++ - Arguments for Exceptions Over Return Codes
Why Does Visual Studio 2013 Error on C4996
C++: When (And How) Are C++ Global Static Constructors Called