how to make temporary objects movable
By defining move constructor/assignment to your type.
Is the return value supposed to be a reference as ap_n&?
Not for operator+ when you return new object.
operator += on the other hand returns reference to lhs, so returns ap_n&.
How can I make my return value usable for move operation. Should the return object be encapsulated by std::move(tmp)? Or is it alright as is?
From return statement,
there is an automatic move when returning directly a local variable.
so return tmp; is sufficient.
return std::move(tmp); prevents NRVO
return tmp += m; does a copy, as you don't return "directly" tmp.
You should do:
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n};
tmp += m;
return tmp; // NRVO, or automatic move
}
return std::move(tmp += m); would prevent NRVO, and do the move.
How does C++ decide when an object is rvalue,
Roughly,
- variables are l-value as there have name.
- function returning l-value reference (
ap_n&) returns l-value.
- function returning r-value reference (
ap_n&&), or by value (ap_n) returns r-value.
or decide that a move operation is suitable on an object, and how can I tell the program that an object is safe to use for move operation.
Overload resolution select the best match between valid candidate.
So it requires function taking by value or by r-value reference (or forwarding reference).
My second question is
It seems not the second ;-)
whether it is necessary to create variants of function that accept rvalue arguments. Right now I have 4 functions, as shown, to be able to accept normal objects and rvalue objects.
Is writing all the combinations possible like this necessary?
Single function taking by const reference or by value can be enough in general case,
unless you want that optimization. so mostly for library writer, or critical code.
Notice that your overloads should be rewritten to effectively do move operation (to reuse input temporary parameter):
ap_n operator+(ap_n&& n, const ap_n& m)
{
n += m;
return std::move(n); // C++11; C++14, C++17
return n; // C++20
}
or
ap_n&& operator+(ap_n&& n, const ap_n& m)
{
return std::move(n += m);
}
If I remove all but the first function, would the program still be able to perform move operation correctly?
With
ap_n operator+(const ap_n& n, const ap_n& m) {
ap_n tmp {n}; // copy
tmp += m;
return tmp; // NRVO, or automatic move
}
You have 1 copy, one NRVO/move for any kind of parameters.
With
ap_n&& operator+(ap_n&& n, const ap_n& m) {
return std::move(n += m);
}
you have no moves, but you should be careful about lifetime of references, as
auto ok = ap_n() + ap_n(); // 1 extra move
auto&& dangling = ap_n() + ap_n(); // No move, but no lifetime extension...
With
ap_n operator+(ap_n&& n, const ap_n& m) {
n += m;
return std::move(n); // C++11, C++14, C++17 // move
return n; // C++20 // automatic move
}
you have 1 move, no copies.
auto ok = ap_n() + ap_n(); // 1 extra move possibly elided pre-C++17, 0 extra moves since C++17
auto&& ok2 = ap_n() + ap_n(); // No moves, and lifetime extension...
So with extra overload, you might trade copy to move.