If I have a constructor with n parameters such that any argument to that can be an rvalue and lvalue. Is it possible to do support this with move semantics for the rvalues without writing 2^n constructors for each possible rvalue/lvalue combination?
You can only define the coursebookname variable one time (which is when you specify the type). Remove the String designation from before the variable name when you pass it to the Person constructor and it should work fine. Person p1 = new Person(cousebookname); Spelling aside.
A move constructor allows the resources owned by an rvalue object to be moved into an lvalue without creating its copy. An rvalue is an expression that does not have any memory address, and an lvalue is an expression with a memory address.
No move constructor is automatically generated.
A move constructor of class T is a non-template constructor whose first parameter is T&&, const T&&, volatile T&&, or const volatile T&&, and either there are no other parameters, or the rest of the parameters all have default values.
You take each one by value, like this:
struct foo { foo(std::string s, bar b, qux q) : mS(std::move(s)), mB(std::move(b)), mQ(std::move(q)) {} std::string mS; bar mB; qux mQ; };
The initialization of the function parameters by the argument will either be a copy-constructor or move-constructor. From there, you just move the function parameter values into your member variables.
Remember: copy- and move-semantics are a service provided by the class, not by you. In C++0x, you no longer need to worry about how to get your own "copy" of the data; just ask for it and let the class do it:
foo f("temporary string is never copied", bar(), quz()); // no copies, only moves foo ff(f.mS, f.mB, f.mQ); // copies needed, will copy foo fff("another temp", f.mB, f.mQ); // move string, copy others
Note: your constructor only takes in values, those values will figure out how to construct themselves. From there, of course, it's up to you to move them where you want them.
This applies everywhere. Have a function that needs a copy? Make it in the parameter list:
void mutates_copy(std::string s) { s[0] = 'A'; // modify copy } mutates_copy("no copies, only moves!"); std::string myValue = "don't modify me"; mutates_copy(myValue); // makes copy as needed mutates_copy(std::move(myValue)); // move it, i'm done with it
In C++03, you could emulate it fairly well, but it wasn't common (in my experience):
struct foo { foo(std::string s, bar b, qux q) // have to pay for default construction { using std::swap; // swaps should be cheap in any sane program swap(s, mS); // this is effectively what swap(b, mB); // move-constructors do now, swap(q, mQ); // so a reasonable emulation } std::string mS; bar mB; qux mQ; };
Take the following code ideone link.
#include <iostream> class A { public: A() : i(0) {} A(const A& a) : i(a.i) { std::cout << "Copy A" << std::endl; } A(A&& a) : i(a.i) { std::cout << "Move A" << std::endl; } int i; }; template <class T> class B1 { public: template <class T1, class T2> B1(T1&& x1_, T2&& x2_) : x1(std::forward<T1>(x1_)), x2(std::forward<T2>(x2_)) {} B1(const B1<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B1" << std::endl; } B1(B1<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B1" << std::endl; } private: T x1; T x2; }; template <class T> class B2 { public: B2(T x1_, T x2_) : x1(std::move(x1_)), x2(std::move(x2_)) {} B2(const B2<T>& x) : x1(x.x1), x2(x.x2) { std::cout << "Copy B2" << std::endl; } B2(B2<T>&& x) : x1(std::move(x.x1)), x2(std::move(x.x2)) { std::cout << "Move B2" << std::endl; } private: T x1; T x2; }; A&& inc_a(A&& a) { ++a.i; return static_cast<A&&>(a); } A inc_a(const A& a) { A a1 = a; ++a1.i; return a1; } int main() { A a1; A a2; std::cout << "1" << std::endl; B1<A> b1(a1,a2); std::cout << "2" << std::endl; B1<A> b2(a1,A()); std::cout << "3" << std::endl; B1<A> b3(A(),a2); std::cout << "4" << std::endl; B1<A> b4(A(),A()); std::cout << "5" << std::endl; B2<A> b5(a1,a2); std::cout << "6" << std::endl; B2<A> b6(a1,A()); std::cout << "7" << std::endl; B2<A> b7(A(),a2); std::cout << "8" << std::endl; B2<A> b8(A(),A()); std::cout << "9" << std::endl; std::cout << std::endl; std::cout << "11" << std::endl; B1<A> b11(a1,a2); std::cout << "12" << std::endl; B1<A> b12(a1,inc_a(A())); std::cout << "13" << std::endl; B1<A> b13(inc_a(A()),a2); std::cout << "14" << std::endl; B1<A> b14(inc_a(A()),inc_a(A())); std::cout << "15" << std::endl; B2<A> b15(a1,a2); std::cout << "16" << std::endl; B2<A> b16(a1,inc_a(A())); std::cout << "17" << std::endl; B2<A> b17(inc_a(A()),a2); std::cout << "18" << std::endl; B2<A> b18(inc_a(A()),inc_a(A())); std::cout << "19" << std::endl; }
Which outputs the following:
1 Copy A Copy A 2 Copy A Move A 3 Move A Copy A 4 5 Copy A Copy A Move A Move A 6 Copy A Move A Move A 7 Copy A Move A Move A 8 9 11 Copy A Copy A 12 Copy A Move A 13 Move A Copy A 14 Move A Move A 15 Copy A Copy A Move A Move A 16 Move A Copy A Move A Move A 17 Copy A Move A Move A Move A 18 Move A Move A Move A Move A 19
As can be seen, the pass by value approach in B2
causes extra move for each argument in all cases except for when the argument is a prvalue.
If you want best performance, I suggest the template approach in B1
. This way you have effectively have separate code for the copy and move cases, and hence only a single copy or a single move required. In the pass by value approach, at least two move/copies are required, except for the prvalue case where the compiler can construct the value in the place of the argument, in which as only one move is required.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With