0

Question:

Assume a function

void doFoo(const std::vector<std::reference_wrapper<DataT> >& data);

where DataT is some type holding some data. I used std::vector just as the typical example of a container class.

What would you say is the most elegant way to call the above function with a std::vector<DataT>?

Background:

Inside the function doFoo(...) I don't want to take ownership of the data. Thus, I don't want to use std::shared_ptr or std::unique_ptr. As I understand it the correct way is to use a reference. If I just use a reference to a std::vector<DataT> I might have to copy several DataT to create the vector for the function call in case I did not construct the data directly in a std::vector<DataT>. Thus, a std::vector<std::reference_wrapper<DataT> > seems to be the best solution.

If I only have a single DataT object (let's call it data1) I can easily put it into the function by calling doFoo({data1}) and the correct vector will be constructed implicitly. But if I already have a std::vector<DataT> dataVec and try to call doFoo(dataVec)it is not implicitly converted to a std::vector<std::reference_wrapper<DataT> >, because the types are completely unrelated.

Possible Solutions:

  1. Create a std::vector<std::reference_wrapper<DataT> > before calling the function and fill it with the references to all elements in dataVec.
    Disadvantage: I have to do this repacking for each function call.
  2. Overload doFoo(...) to take a std::vector<DataT> and do the repacking there.
    Disadvantage: If I use this concept in many places it creates quite some boilerplate code. Also, to me it seems a bit redundant and maybe even confusing to have that extra function call.
  3. Enable some implicit conversion from std::vector<DataT> to a std::vector<std::reference_wrapper<DataT> >. But I don't know whether this is possible at all, and if it might have some bad consequences. If this or something similar is possible and safe I would prefer this solution.
user2296653
  • 1,151
  • 1
  • 8
  • 17
  • "If I just use a reference to a std::vector I might have to copy several DataT to create the vector for the function call". Not so - using `const std::vector& data` doesn't do any copying of the internal state of the vector its being bound to. – Alejandro Jul 10 '15 at 16:18
  • 1
    @Alejandro I read his question comment as saying he might have some DataT objects that aren't in a vector and he wants to call doFoo on them. He doesn't want to have to copy them in the vector in order to call doFoo – pythonic metaphor Jul 10 '15 at 16:25
  • Sorry for not being clear: I meant that when I have the data already, but they are not packed into a `std::vector`, or even if I have an object of that type but only want to use a subset of the data, I would have to create a new `std::vector` and fill it by copying the data, before I can actually call the function. (Thanks, @pythonicmetaphor, I wrote my comment before seeing yours.) – user2296653 Jul 10 '15 at 16:30
  • 1
    Is `doFoo` required to have that signature? I think that `std::vector` is simpler than using `reference_wrapper` this way, a `DataT*` perfectly expresses a reference to a thing you don't own. If changing `doFoo` is possible then another option is to change it to take a pair of iterators so you have more flexibility what you pass to it, and don't have to construct a container of exactly the right type. – Jonathan Wakely Jul 10 '15 at 16:53
  • 1
    i.e. I question the premise that you set up right at the start: _"Assume a function"_ ... why assume such a strange-looking function? – Jonathan Wakely Jul 10 '15 at 17:02
  • @JonathanWakely You are right, I can also change the function signature. I just nailed down the function in a way that made most sense to me; I tried to explain in "Background" why I'm using this type. Using `std::vector` would lead to the same "problems" in the function call as a vector of reference_wrappers. – user2296653 Jul 10 '15 at 17:09
  • Yes, that's why I made a comment not an answer, `vector` still needs you to construct that vector, but it's much more conventional and makes more sense than (mis)using reference_wrappers. Also note that you might want `vector>` or `vector` if the intention is that `doFoo` doesn't modify the objects! – Jonathan Wakely Jul 10 '15 at 17:17

1 Answers1

1

I see some options here.

1. Use the iterator constructor of the vector.

std::vector<DataT> x;
doFoo({x.begin(), x.end()});

which should construct a vector of reference wrappers from the vector since the iterators reference the objects anyway.

2. Use std::vector<DataT> const & as doFoo argument and move your data into the vector.

If you have your data somewhere around you can move them into the vector instead of copying (if moving DataT makes sense).

DataT d1, d2, d3;
std::vector<DataT> v { std::move(d1), std::move(d2), std::move(d3) };
dooFoo(v);

Which transfers the ownership to the vector (but not to the function)!

3. Generally operate on DataTs in a vector if there's more than one to be handled.

If you just don't have multiple DataT objects around that would require being copied into the vector there is no need for the reference_wrapper.

Pixelchemist
  • 24,090
  • 7
  • 47
  • 71
  • Your option 2 doesn't seem like a good idea, now you have modified `d1`, `d2` and `d3` just to pass them to a function that wasn't going to modify anything (because it only got const references to the data). What if other code needs to use those variables later? And what is the advantage of your `vector_emplace` function compared to `std::vector v{ std::move(d1), std::move(d2), std::move(d3) };`? Even if you want a function, it shouldn't be recursive, it can just do `return { std::forward(values)... };` – Jonathan Wakely Jul 10 '15 at 16:57
  • @JonathanWakely Well the function was a bit nasty but of course: There is a transfer of ownership into the vector but that's obvious. Any code requiring the data later must operate on the vector instead of d1-3. But its not a temporary to the function call but a vector present in the current scope. – Pixelchemist Jul 10 '15 at 17:12