Welcome to the next pikoTutorial!
std::move moves an object
One of the most prevalent misconceptions is that std::move actually moves an object. In reality, std::move does not perform any movement – it casts its argument to an rvalue reference. This enables the compiler to apply move semantics, but std::move itself does not move anything. The actual move operation is performed by the move constructor or move assignment operator of the object. In simple words – if the move constructor of an object does not execute any movement of its internals, std::move won’t change that fact.
After std::move object becomes empty or invalid
Another common misconception is that objects passed to std::move are always left in an invalid state. While it’s true for some objects like smart pointers which are nullified, there are many types which are still valid after moving. For example, an int is a primitive type, it doesn’t have a constructor or destructor, so it still holds the value even after moving:
int value = 12;
int moved_value = std::move(value);
std::cout << moved_value << std::endl; // shows 12
std::cout << value << std::endl; // original variable still holds 12Also in case of complex types which don’t specify any move-specific behavior, you can expect the original object to preserve its state. For example, if I create a simple struct Point and don’t implement a custom move constructor, the compiler-generate move constructor won’t include any modification of the “moved-from” object:
struct Point
{
int x, y;
};
int main()
{
Point point {12, 24};
Point moved_point = std::move(point);
std::cout << moved_point.x << ", " << moved_point.y << std::endl; // prints "12, 24"
std::cout << point.x << ", " << point.y << std::endl; // still prints "12, 24"
}std::move is required to trigger move semantics
Some developers mistakenly believe that std::move is always necessary to avoid copying objects. While std::move is crucial for explicitly invoking move semantics, compilers are often able to elide copies and perform moves implicitly, thanks to Return Value Optimization (RVO) and Named Return Value Optimization (NRVO). In many cases, relying on the compiler’s optimizations can result in cleaner and more efficient code.
std::string CreateString() {
std::string local = "Hello, World!";
return local; // RVO makes std::move unnecessary here
}std::move can force non-movable types to be moved
I heard once that you can use std::move on a non-movable type to make it movable . This is not true because std::move is not responsible for the actual moving logic. Move semantics only apply to types that have move constructors and move assignment operators implemented.
class NonMovable
{
public:
NonMovable() {}
NonMovable(NonMoveable&&) = delete; // deleted move constructor
};
int main()
{
NonMovable obj1;
NonMovable obj2(std::move(obj1)); // compilation error
}std::move triggers move semantics always
When std::move is applied to a const object, it casts the object to an rvalue reference, but since the object is const, its move constructor cannot be invoked. Instead, the copy constructor is used to create a new object. This is because move constructors typically modify the source object, which is not allowed for const objects. Thus, attempting to move a const object results in a copy operation, not a move. Understanding this nuance is crucial for correctly applying move semantics and avoiding unintended performance hits.
#include <iostream>
class SomeClass
{
public:
SomeClass() {}
SomeClass(const SomeClass&) { std::cout << "Copy constructor" << std::endl; }
SomeClass(SomeClass&&) {std::cout << "Move constructor" << std::endl; }
};
int main()
{
const SomeClass obj1;
SomeClass obj2(std::move(obj1)); // prints "Copy constructor"
}Read also:
- GTest and short-circuit evaluation in C++
- AI is powerful. Snippets are instant.
- From AUTOSAR to S-Core: the first C++ pub/sub implementation
- How to write Arduino Uno code with Python?
- Combining Bazel with Docker
- Running commands with timeout on Linux
- Running Python unit tests with CMake
- Thirdparty dependencies with FetchContent
- Bug of the week #11
- Combining CMake with Docker
- How to search the internet from Linux terminal?
- Folding expressions in C++
- How to derive from an enum in Python?
- Bug of the week #10
- Trying ROS2: client/server within a single container
- Make C++ a better place #4: Go as an alternative
- How to convert hex to dec in Linux terminal?
- Setting up a Python project with CMake
- Separating builds for different configs with Bazel
- Trying ROS2: pub/sub within a single container
- Bug of the week #9
- UDP multicasting with Python
- Destruction order vs thread safety in C++
- Let’s review some code: C++ #2
- Make C++ a better place #3: D as an alternative
- Registering callback using std::function in C++
- Bug of the week #8
- TCP client/server with Python
- Simple menus in Bash scripts with select
- Calling member function on a nullptr in C++
- Bug of the week #7
- Python lru_cache explained
- How to dockerize a Python application?
- Make C++ a better place #2: CppFront as an alternative
- Parameters combinations in GoogleTest
- Data transfer with curl
- Python reduce explained
- Bug of the week #6
- Custom literals in C++
- Linux and hash command
- 5 Python good practices which make life easier
- Let’s review some code: Python #1
- Make C++ a better place #1: What does better mean
- Enums vs enum class in C++
- Bug of the week #5
- UDP client/server with Python
- Hard links in Linux
- Functions calling order in unit tests in C++
- Bug of the week #4
- Yield in Python – state machines, coroutines and more
- Copy files from another branch with Git
- Make C++ a better place #0: Introduction
- 5 misconceptions about std::move in C++
- How to use xargs on Linux?
- How to test method call order with unittest in Python?
- Bug of the week #3
- Build & run C++ unit tests with CMake
- Arrange text with sort on Linux
- Key derivation function with Python
- Let’s review some code #1: C++









