2

So I have an aggregate class Foo which takes moveable and (indirectly) copyable arguments (A class called Image_t, and a std::unique_ptr<Map>, and Map can clone itself).

I want to give the end user flexibility in how they create the class. So I have a "copy-like" constructor and a "move-like" constructor:

  typdef std::unique_ptr<Map> MapPtr;

  ///
  ///@brief Copy build constructor
  ///
  Foo(const Image_t& image, const MapPtr& map_ptr) :
  image_(image),
  geo_locatormap_(map_ptr->clone())
  {

  }

  ///
  ///@brief Move build constructor
  ///
  Foo(Image_t&& image, MapPtr&& map_ptr) :
  image_(std::move(image))
  geo_locatormap_(std::move(map_ptr))
  {

  }

Now ideally, I'd like to have Foo(const Image_t& image, MapPtr&& map_ptr) and Foo(Image_t&& image, const MapPtr& map_ptr) as well, but I'm feeling like this is duplicating effort. Is there a quick way to do this? Or is this type of constructor frowned upon?

IdeaHat
  • 7,641
  • 1
  • 22
  • 53

3 Answers3

3

Use type deduction, perfect forwarding and a little helper:

static MapPtr&& map_forward( MapPtr&& m ) { return std::move( m ); }
static MapPtr map_forward( const MapPtr& m ) { return m->clone(); }

template<typename I, typename M>
Foo(I&& i, M&& m) :
image_{std::forward<I>(i)},
geo_locatormap_{map_forward(std::forward<M>(m))}
{

}
Daniel Frey
  • 55,810
  • 13
  • 122
  • 180
  • Very nice answer. I didn't think to use an external helper function in my example below. Yours is a lot cleaner. – bstamour Oct 22 '13 at 13:38
  • @bstamour Thanks. I often use little helpers as they also scale better, think what happens if you have three or four of those parameters. – Daniel Frey Oct 22 '13 at 13:48
  • Very true. I'm going to add that idea to my toolbox ;) – bstamour Oct 22 '13 at 13:49
  • Thank you for the answer! Just when I think I get RHR, this throws me for a loop...So I'm guessing the template parameter is to pick up const-ness, but I am a little confused at how, if I have `const Image_t a; Foo foo(a,std::move(some_ptr));`, how do the template parameters resolve? Do you got a link that explains this? – IdeaHat Oct 22 '13 at 13:52
  • 1
    @MadScienceDreams This is called [**perfect forwarding**](http://stackoverflow.com/questions/3582001), the link explains it in depth. It's a bit complicated but there is just no simple way to explain it. – Daniel Frey Oct 22 '13 at 13:58
1

In your case, since you can't just perfectly forward the map_ptr through (unless you use a helper function like in Daniel Frey's answer), you will need to handle that case separately. One method I can think of to cut your constructor count down to two is something like this:

// Clone the map.
template <typename Image>
Foo(Image&& img, MapPtr const& map_ptr)
    : image_{ std::forward<Image>(img) },
      geo_locatormap_{ map_ptr->clone() }
{ }

// move the map.
template <typename Image>
Foo(Image&& img, MapPtr&& map_ptr)
    : image_{ std::forward<Image>(img) },
      geo_locatormap_{ std::move(map_ptr) }
{ }

The Image&& syntax will bind to any lvalue/rvalue reference, and with std::forward you'll get the best operation for the type.

bstamour
  • 7,746
  • 1
  • 26
  • 39
0

It is enough to define only one version: Foo(Image_t&& image, MapPtr&& map_ptr)

Take a look for example at this link for more details.

Igor Popov
  • 2,588
  • 17
  • 20