Move semantics == custom swap function obsolete?
It is a matter of judgment. I will typically let std::swap
do the job for prototyping code, but for release code write a custom swap. I can usually write a custom swap that is about twice as fast as 1 move construction + 2 move assignments + 1 resourceless destruction. However one may want to wait until std::swap
actually proves to be a performance problem before going to the bother.
Update for Alf P. Steinbach:
20.2.2 [utility.swap] specifies that std::swap(T&, T&)
has a noexcept
equivalent to:
template <class T>
void
swap(T& a, T& b) noexcept
(
is_nothrow_move_constructible<T>::value &&
is_nothrow_move_assignable<T>::value
);
I.e. if move operations on T
are noexcept
, then std::swap
on T
is noexcept
.
Note that this spec doesn't require move members. It only requires that construction and assignment from rvalues exists, and if it is noexcept
, then swap will be noexcept
. E.g.:
class A
{
public:
A(const A&) noexcept;
A& operator=(const A&) noexcept;
};
std::swap<A>
is noexcept, even without move members.
Is specializing std::swap deprecated now that we have move semantics?
The specialization of std::swap
is now optional, but not deprecated. The rationale is performance.
For prototyping code, and perhaps even for much shipping code, std::swap
will be plenty fast. However if you're in a situation where you need to eek out every little bit from your code, writing a custom swap can still be a significant performance advantage.
Consider the case where your class essentially has one owning pointer and your move constructor and move assignment just have to deal with that one pointer. Count machine loads and stores for each member:
Move constructor: 1 load and 2 stores.
Move assignment: 2 loads and 2 stores.
Custom swap: 2 loads and 2 stores.
std::swap
is 1 move construction and 2 move assignments, or: 5 loads and 6 stores.
A custom swap is potentially still two or three times faster than std::swap
. Though any time you're trying to figure out the speed of something by counting loads and stores, both are going to be wicked fast.
Note: In computing the cost of your move assignment, be sure and take into account that you will be moving into a moved-from value (in the std::swap
algorithm). This often negates the cost of a deallocation, though at the cost of a branch.
When to provide custom swap function in C++?
You should follow the rule of 0.
Non-resource managing classes should have default move/copy/assign/destruction.
Resource managing classes should be only concerned with managing resources.
Your code does not manage resources. Everything copy/move wise should be defaulted.
swap
is a special case of move. In the case you are a resource managing type, you may want to also write it. However, like most things, it is an optimization, and you shoud only do such optimizations if you have proven it has performance impact.
An example of when it might have performance impact is if you have a never-empty resource-owning type and that resource isn't free, so creating a temporary object has unavoidable costs. std::swap
by default moves through a throw-away object. A custom swap could avoid this.
Or, a type that is a large buffer of cheap to move elements; creating that large buffer could have real costs.
Otherwise, don't do it. And test if you do it that it helps. Custom swap is a optimization of move for a particular case.
Rationale behind the usual implemention of std::swap overloads
since S members are built-in types, is it fine (well-formed) to define it as below instead: [std::swap usage]
Yes. I mean, it works because std::swap
just "swaps the values a and b" if they are both MoveAssignable
and MoveConstructible
, which built-in types are.
If the members are all built-in types (or pointer types, possibly pointers to custom types), is it still necessary/recommended to use that idiom instead of explicitly calling std::swap() ?
Just for the future of your class: if one day any of its members gets replaced with a user-defined type, you might forget to modify your swap
and miss a "specialized" ADL implementation for it, still calling the generic std::swap
which could (theoretically) be inefficient yet still compile.
Actually, you don't even need to think about it if you just always use boost::swap
or implement it yourself:
template<typename T> void swap(T& a, T& b) {
using std::swap;
swap(a, b);
}
Using move semantics to avoid copying when pushing_back to a custom container does not avoid copying
When you call this line
myContainer.push_back(foo(a));
L-value is passed into push_back
method, and now read about using std::forward - http://www.cplusplus.com/reference/utility/forward/,
Returns an rvalue reference to arg if arg is not an lvalue reference.
If arg is an lvalue reference, the function returns arg without modifying its type.
and in your push_back
you call
new(data + size) T(std::forward<U>(value));
but value
was passed as L-value, and only constructor MyClass(const MyClass &passedEntity)
can be invoked.
If you want a
object to be moved, you can write
myContainer.push_back(std::move(a)); // cast to R-reference
EDIT
You should not use move in your push_back
function, below is simple example.
Suppose you have class like this:
struct Foo {
int i;
Foo (int i = 0) : i(i) {
}
~Foo () {
}
Foo (const Foo& ) {
}
Foo& operator=(const Foo&) {
return *this;
}
Foo (Foo&& f)
{
i = f.i;
f.i = 0; // this is important
}
Foo& operator=(Foo&& f)
{
i = f.i;
f.i = 0; // this is important
return *this;
}
};
we have also 2 functions
template<class T>
void process1 (const T& ) {
cout << "process1" << endl;
}
template<class T>
void process (T&& obj) {
cout << "process2" << endl;
T newObj = forward<T>(obj);
}
and bars functions are counterparts of your push_back
method.
template <typename T>
void bar1 (T&& value) {
process (move(value)); // you use move in your push_back method
}
template <typename T>
void bar2 (T&& value) {
process (forward<T>(value));
}
now we must consider 4 cases:
[1] pass L-value, version with forward
Foo f(20);
bar2 (f);
cout << (f.i) << endl; // 20
[2] pass R-value, version with forward
Foo f(20);
bar2 (move(f));
cout << (f.i) << endl; // 0, it is OK bacuse we wanted to move 'f' object
[3] pass R-value, version with move
Foo f(20);
bar1 (move(f));
cout << (f.i) << endl; // 0, ok, we wanted to move 'f' object
[4] pass L-value, version with move in your push_back method
Foo f(20);
bar1 (f);
cout << (f.i) << endl; // 0 !!! is it OK ? WRONG
in last case, we passed f
as L-value, but this object was moved in bar1
function, for me this is strange behavior and is incorrect.
Related Topics
Is It Counter-Productive to Pass Primitive Types by Reference
Determine Path to Registry Key from Hkey Handle in C++
Doing a Static_Assert That a Template Type Is Another Template
Multiple Definition of Inline Functions When Linking Static Libs
Unresolved External Symbol Lnk2019
C++11: Compile-Time Array with Logarithmic Evaluation Depth
Visual Studio 2017 Errors on Standard Headers
Should We Generally Use Float Literals for Floats Instead of the Simpler Double Literals
So Why Is I = ++I + 1 Well-Defined in C++11
C++: When (And How) Are C++ Global Static Constructors Called
Populate an Array Using Constexpr at Compile-Time
Vector of Std::Function with Different Signatures
How to Alloc a Executable Memory Buffer
A Fast Method to Round a Double to a 32-Bit Int Explained
How to Enable C++17 Support in VScode C++ Extension