7

I've spend quite some time on implementing move semantics for my class but now I'm dealing with functions that use it.

Ok, so I have this object which has a lot of data on the heap: CLargeOb for which I implemented the move semantics (constructor and operator =). It is ideally used like this:

void OtherOb::Func(CLargeOb&& largeOb1, CLargeOb&& largeOb2)
{
    SomeOtherFunc(largeOb1); // use objects
    SomeOtherFunc(largeOb2); 
    m_largeOb1 = (CLargeOb&&)largeOb1; // save as members and trash the originals
    m_largeOb2 = (CLargeOb&&)largeOb2;
}

However it's not always possible to allow the objects to be moved/trashed, so I added these two functions:

void OtherOb::Func(const CLargeOb& largeOb1, CLargeOb&& largeOb2)
{
    SomeOtherFunc(largeOb1);
    SomeOtherFunc(largeOb2); 
    m_largeOb1 = largeOb1;
    m_largeOb2 = (CLargeOb&&)largeOb2;
}

void OtherOb::Func(CLargeOb&& largeOb1, const CLargeOb& largeOb2)
{
    SomeOtherFunc(largeOb1);
    SomeOtherFunc(largeOb2); 
    m_largeOb1 = (CLargeOb&&)largeOb1;
    m_largeOb2 = largeOb2;
}

Although it works, you can already guess it will become a major pain in the *ss when I have a function which takes 3 or more of these objects as parameters... Isn't there a clever way to solve this using templates or maybe 'perfect forwarding'?

demorge
  • 1,097
  • 1
  • 7
  • 17
  • @MooingDuck: works fine in VS2010, no problem. To invoke the operator =(&&) you must call it explicitly using std::move or the && cast. But the solution provided below it much better of course. – demorge Aug 03 '12 at 21:40
  • @demorge: oh right, I completely forgot the rules change when it's named. Sorry for that – Mooing Duck Aug 03 '12 at 21:45
  • 1
    Please write `std::move(largeOb1)` instead of `(CLargeOb&&)largeOb1`. Generations of programmers to come will thank you for using the standard C++11 way of enabling a move instead of a non-idiomatic and borderline unreadable C/C++ mix. – fredoverflow Aug 04 '12 at 12:15
  • Also, you may be interested in the following [FAQ entry](http://stackoverflow.com/a/11540204/252000) on move semantics. – fredoverflow Aug 04 '12 at 12:16

1 Answers1

15

As was the case in C++03, the guideline is: if you want a copy, make it in the parameter list.

This lets the caller deal with how you get the object, you just get an object regardless:

void OtherOb::Func(CLargeOb largeOb1, CLargeOb largeOb2) 
{ 
    SomeOtherFunc(largeOb1); // use objects 
    SomeOtherFunc(largeOb2);  
    m_largeOb1 = std::move(largeOb1); // save as members and trash the originals 
    m_largeOb2 = std::move(largeOb2); // (you should use std::move, not cast)
} 

The caller:

OtherOb o;

CLargeOb x, y;
const CLargeOb z;

o.Func(x, std::move(y)); // need x for later, done with y so move it
o.Func(std::move(x), z); // done with x, necessarily copy z

This is just as efficient as several specialized overloads. Why? Because those already exist in the class as constructors. Let the compiler figure out which to call at the call site for you, it already knows what to do.

GManNickG
  • 494,350
  • 52
  • 494
  • 543
  • Doesn't this move twice, instead of once? (NB: Of course, one additional move should be barely noticeable so I've upvoted anyway) – Sjoerd Aug 03 '12 at 22:51
  • @Sjoerd: Yes, the hope is that the compiler will elide a move somewhere; and as you said even if it doesn't, it shouldn't matter. – GManNickG Aug 03 '12 at 22:56