32

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?

4
  • 1
    Give the code example to make it more clear. (who knows, may be you don't need move constructor at all!) Commented Jul 14, 2011 at 4:50
  • 2
    Can you show your current code? As far as I know, the move constructor is the "special" constructor Foo(Foo&& that), so I'm a little confused about what you're talking about. Commented Jul 14, 2011 at 4:52
  • @zneak: move constructor will take references to the same class only, i think Sid is talking about passing any 'n' parameters to the constructor. @Sid: if you try to pass each parameter into separate functions(not the constructor), then you will have a worst case of 2*n functions, one for rvalue and one for lvalue for each argument. Commented Jul 14, 2011 at 5:05
  • 1
    @zneak, @Aditya: Yeah, I was talking about passing any 'n' parameters to the constructor. I was in a hurry so I entitled the question terribly. I'll fix that. Commented Jul 14, 2011 at 8:36

3 Answers 3

38

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;
};
Sign up to request clarification or add additional context in comments.

24 Comments

+1 Great, in the new world, you just take your arguments by value and everything will be as good as it can be. That's wonderful. Could you add the assignment operator as well for fun?
@Kerrek: You take values if you need copies of the arguments, but not if you only need to inspect them (const reference) or modify them (reference). I'm sure you probably know this, but someone reading your comment might not.
mutates_copy("no copies, only moves!") does copy string literal in std::string ctor, there is no way to move a string literal to an std::string.
foo(std::string s, bar b, qux q) : mS(s)... works even better, since compiler can elide copy ctor, when a source is an rvalue, which is better than move.
@Gene: But, you're not dealing with rvalues. You may have had rvalues passed into the foo constructor. Those would then be moved into the lvalues s, b and q. In your version, you use s directly to construct mS. s is not an rvalue, so neither elision nor move construction happens. You can demonstrate it with noisy constructors. ideone.com/CRbaA
|
4

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.

Comments

-3

Depending on what c++ compiler you are using, you could look into "functions with variable argument lists"

The idea is that you can pass in as many parameters as you want to the method and it just populates into an array that you can loop through.

For microsoft c++, the following blogposts might be helpful:

http://msdn.microsoft.com/en-us/library/fxhdxye9(v=VS.100).aspx http://blogs.msdn.com/b/slippman/archive/2004/02/16/73932.aspx

2 Comments

the number of parameters are fixed (and known). The OP's question is how to exploit move semantics instead of copying the arguments :)
NO C++0x program should be using old unsafe varargs, the new C++0x way to do such things is variadic templates, for typesafe functions with variable argument count. Besides that, varargs are totally unrelated to the OPs question.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.