Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing/Moving parameters of a constructor in C++0x

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?

like image 566
Opt Avatar asked Jul 14 '11 04:07

Opt


People also ask

How do you pass parameters to a constructor?

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.

What is a move constructor?

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.

Is move constructor automatically generated?

No move constructor is automatically generated.

What is default move constructor?

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.


2 Answers

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; }; 
like image 113
GManNickG Avatar answered Sep 28 '22 08:09

GManNickG


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.

like image 34
Clinton Avatar answered Sep 28 '22 07:09

Clinton