When you consider moving only one parameter, you might get away with declaring function overloads, one of which would be moving from a temporary. Or you have design where oly temporaries are used, then why don't you follow an "emplace" strategy instead and create new object on-site?
But with two or more parameters moved the number of required overloads would be four, eight and so on. That's not good, in that case perfect forward might be more useful. An example of forwarding single parameter (of type Spec
) in C++11 style:
#include <iostream>
#include <utility>
struct Data {
Data(const Data&) { std::cout << "Data copied\n"; }
Data() { std::cout << "Data created\n"; }
};
struct Spec {
Data *ptr;
Spec() : ptr(new Data()) {};
Spec(const Spec& other) : ptr(new Data{*other.ptr}) {};
Spec(Spec && other) : ptr(other.ptr) {
other.ptr = nullptr;
std::cout << "Data moved\n";
}
Spec& operator=(const Spec& other) { ptr = new Data{*other.ptr};
std::cout << "Data copied\n";
return *this; }
Spec& operator=(Spec&& other) { ptr = other.ptr; other.ptr = nullptr;
std::cout << "Data moved\n"; return *this;
}
~Spec() { delete ptr; }
};
struct foo {
Spec d;
template < typename T, std::enable_if_t<std::is_convertible<T, Spec>::value> * = nullptr>
foo(T&& v) : d(std::forward<T>(v)) { }
template <typename T>
auto set_spec(T&& v) -> decltype(v = std::forward<Spec>(v), void())
{ d = std::forward<T>(v); }
};
int main()
{
std::cout << "Move\n";
foo a {Spec()};
a.set_spec(Spec());
std::cout << "Copy\n";
Spec s;
foo b {s};
a.set_spec(s);
}
You have to modify whole chain of responsibility to use that, starting with overloading Inventory's method:
void addGuitar(const string &serialNumber, double price, GuitarSpec&& spec) {
// move, do we want move string?
inventory.emplace_back(serialNumber, price, std::move(spec));
}
Or using perfect forwarding, this template can copy OR move, when appropriate (example without SFINAE):
template <class SN, class SP>
void addGuitar(SN&& serialNumber, double price, SP&& spec)
{
inventory.emplace_back(std::forward<std::string>(serialNumber),
price, std::forward<GuitarSpec>(spec));
}
Technically addGuitar
might just be that if we don't want to bother about restricting interface by SFINAE, assuming we would always use it right and nothing wrong may happen (Murphy, put your hand down) if it's not a public interface. A bad assumption in large project with long life and multiple developers.