5

I want to store a list of objects in an std::vector, but the objects contain a reference and cannot be assigned to. I can, however, copy-construct the object.

The only option I can think of is to use pointers to wrap the objects and reseat the pointers when they need to be assigned, but the syntax for this would significantly reduce readability, especially when using iterators, and I would prefer an alternative.

Doesn't work:

std::vector<MyObject> myVector;
//fill the vector
//...
myVector[1] = object1;

Smart pointers sacrifice readability:

std::vector<std::unique_ptr<MyObject>> ptrVector;
//fill the vector
//...
ptrVector[1] = std::unique_ptr<MyObject>(new MyObject(object1));

Are there any other methods to use unassignable objects in an std::vector?

Elliot Hatch
  • 1,038
  • 1
  • 12
  • 26
  • Assuming C++11, would using `std::reference_wrapper` instead of conventional references be a solution? – jogojapan Oct 16 '12 at 03:08
  • I don't think you need the `std::move` in the last line of code, as `std::unique_ptr(...)` is an rvalue. – David Rodríguez - dribeas Oct 16 '12 at 03:10
  • @DavidRodríguez-dribeas Oh yeah, fixed that in the question. – Elliot Hatch Oct 16 '12 at 03:11
  • Related question and answer that mention `std::list` as alternative (of course at the cost of O(n) insertion complexity): http://stackoverflow.com/questions/9853762/how-to-push-back-without-operator-for-const-members – jogojapan Oct 16 '12 at 03:12
  • Apart from the fact that smart pointers make the code harder to read, I don't understand why you consider them as a possible solution in the first place. As your code shows, using them implies creating new objects, rather than pointers/references to existing ones. – jogojapan Oct 16 '12 at 03:15
  • 1
    @jogojapan This question is related to creating new objects. When I assign an object to a vector I'm copying the information into the existing object, which is logically identical to creating a new object with the information and giving the vector a reference to the new object. – Elliot Hatch Oct 16 '12 at 03:20
  • @Rabenholz Right, sorry. I somehow assumed you meant to get rid of the need to store references in the objects by using smart pointers instead. Misunderstanding on my part. Anyway, would `std::reference_wrapper` as member of the objects help? – jogojapan Oct 16 '12 at 03:22
  • @jogojapan I'll look into that. I've never seen reference wrappers before so I didn't realize that sort of thing was an option. – Elliot Hatch Oct 16 '12 at 03:28
  • @Rabenholz I have another idea that may help you. But I am not sure about it yet, so I proposed it in the form of a new question here: http://stackoverflow.com/questions/12908398/can-placement-new-and-vectordata-be-used-to-replace-elements-in-a-vector – jogojapan Oct 16 '12 at 06:15

2 Answers2

2

This isn't a direct answer to your question as I can't offer a replacement for std::vector, or a different way of using it that allows you to do what you need to.

However, if it is possible to modify the definition of MyObject, it may be an option to change it so it uses std::reference_wrapper instead of conventional references. That would allow MyObject to be assignable.

Example:

#include <vector>
#include <functional>
#include <iostream>

struct MyObject
{
  //int &_i;
  std::reference_wrapper<int> _i;
  MyObject(int &i):_i(i) {}
};


int main() {
  std::vector<MyObject> vec;
  int i1 = 3;
  int i2 = 4;
  MyObject c1(i1);
  MyObject c2(i2);

  /* Storing object. */
  vec.push_back(c1);

  /* Assigning to it. */
  vec[0] = c2;

  /* Confirming that it's the correct object now. */
  for (const MyObject &it : vec)
    std::cout << it._i << std::endl;

  /* Modifying the value of its internal reference. */
  vec[0]._i.get() = 5;
  /* Confirming that the original int changed value indeed. */
  std::cout << "i2 == " << i2 << std::endl;

  return 0;
}

Caveat: Existing code may already contain direct assignments to the reference member (i.e. the member called _i in the code above). Those assignments were intended to change the value of the object the reference refers to. When replacing the reference with a std::reference_wrapper, all direct assignments _i = x must be replaced with _i.get() = x, otherwise the semantics of the program change entirely.

(EDIT) If the references used are const-references const T&, a std::reference_wrapper<const T> can be used. Using the example above, the definition of MyObject then changes to this:

struct MyObject
{
  std::reference_wrapper<const int> _i;
  MyObject(const int &i):_i(i) {}
};
jogojapan
  • 68,383
  • 11
  • 101
  • 131
  • I just realized that the object contains a `const` reference, so changing the references to wrappers won't help here. I've edited my question to reflect the change. – Elliot Hatch Oct 16 '12 at 04:00
  • You can use a `std::reference_wrapper` (and replace `int` with the type you are using) in that case. – jogojapan Oct 16 '12 at 04:06
  • Oh, haha, when I was testing this I used a `const std::reference_wrapper`. I've reverted the question. Thanks for all your help! – Elliot Hatch Oct 16 '12 at 04:12
  • If I were using a `const` variable that isn't a reference e.g. `const int` this method wouldn't work, would it? – Elliot Hatch Oct 16 '12 at 04:39
  • @Rabenholz How exactly do you use that variable? As a member of `MyObject`? Or in a different way? (If it's a member of `MyObject`, then indeed there is no way to make `MyObject` assignable.) – jogojapan Oct 16 '12 at 05:00
  • Okay. I guess I was wondering about a more general case, where the object cannot be assigned to, no matter what, whether it be a class you don't have access to or the class has a const member. As far as I can tell, the solution I propose in the question can be used in any nonassignable case, but your answer is a very useful alternative for most cases where I might encounter this problem. Thanks again for your help. – Elliot Hatch Oct 16 '12 at 05:17
  • Oh, and just for closure, I think I'm going to use my original method because the variable in question is a protected member in a base class in a library, and the slightly different syntax for reference wrappers would cause problems in most derived classes (yet another reason to use accessors :P). I'll accept this as an answer tomorrow if there are no answers regarding the more general case I was referring to. – Elliot Hatch Oct 16 '12 at 05:26
  • 1
    In a new version of the library I've changed the const reference to a reference wrapper as you suggested. The primary usage of the references was as function parameters, so I barely had to change any code to account for this. Thanks for the great advice! – Elliot Hatch Oct 31 '12 at 05:54
0

Why not make "MyObject" assignable? You can override the operator "=", thus "myVector[1] = object1" will be workable.

Aladdin
  • 171
  • 8