Pass by value, then move is actually a good idiom for objects that you know are movable.
As you mentioned, if an rvalue is passed, it'll either elide the copy, or be moved, then within the constructor it will be moved.
You could overload the copy constructor and move constructor explicitly, however it gets more complicated if you have more than one parameter.
Consider the example,
class Obj {
public:
Obj(std::vector<int> x, std::vector<int> y)
: X(std::move(x)), Y(std::move(y)) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
Suppose if you wanted to provide explicit versions, you end up with 4 constructors like so:
class Obj {
public:
Obj(std::vector<int> &&x, std::vector<int> &&y)
: X(std::move(x)), Y(std::move(y)) {}
Obj(std::vector<int> &&x, const std::vector<int> &y)
: X(std::move(x)), Y(y) {}
Obj(const std::vector<int> &x, std::vector<int> &&y)
: X(x), Y(std::move(y)) {}
Obj(const std::vector<int> &x, const std::vector<int> &y)
: X(x), Y(y) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
As you can see, as you increase the number of parameters, the number of necessary constructors grow in permutations.
If you don't have a concrete type but have a templatized constructor, you can use perfect-forwarding like so:
class Obj {
public:
template <typename T, typename U>
Obj(T &&x, U &&y)
: X(std::forward<T>(x)), Y(std::forward<U>(y)) {}
private:
std::vector<int> X, Y;
}; // Obj
References:
- Want Speed? Pass by Value
- C++ Seasoning
std::move
on aconst&
returns aconst&&
that cannot be moved from. – Decalescence