How can an instance add itself to a vector?

Viewed 87

I need to instantiate a class SpriteWithTimer and that the new object adds itself to a vector vsprites.

Here's a code snippet:

#include <vector>
...
class SpriteWithTimer {
   public:
   SpriteWithTimer();
   ~SpriteWithTimer();
   ...
};
static std::vector<SpriteWithTimer> vsprites;

int main()
{
   ...
}
...
SpriteWithTimer::SpriteWithTimer(){
        ...
        vsprites.push_back(this);
   };

but I've got his error:

no matching function for call to ‘std::vector<SpriteWithTimer>::push_back(SpriteWithTimer*)’
no instance of overloaded function "std::vector<_Tp, _Alloc>::push_back [with _Tp=SpriteWithTimer, _Alloc=std::allocator<SpriteWithTimer>]" matches the argument list -- argument types are: (SpriteWithTimer *) -- object type is: std::vector<SpriteWithTimer, std::allocator<SpriteWithTimer>>

thanks!

2 Answers

The object stored inside a vector is always a brand new separately-constructed object. Therefore, you can't store to a vector an object created elsewhere. Either, you can store:

  1. a copy of that object: push_back(*this),
  2. an object with the content moved from that object: push_back(std::move(*this)),
  3. a pointer to that object push_back(this).

You need to choose what is suitable in your case. In the third case, beware of dangling pointers. Basically, you should guarantee that the lifetime of the pointed-to objects will not end before these pointers are used.

A vector of Foo objects?

If you want a vector of, say Foo objects (and not pointers to Foo objects), you would need to dereference the this pointer within the member function that is intended to copy the object to the pass-in non-const container reference. E.g.:

#include <vector>

struct Foo {
    int x{42};
    void addMeToVector(std::vector<Foo>& v) & { 
        // _copies_ the `Foo` object in v
        v.push_back(*this); 
    }
};

int main() {
    std::vector<Foo> v;
    Foo f1{};
    f1.addMeToVector(v);
}

Note that we have applied the ref-qualifier & to the addMeToVector member function, to avoid a temporary object attempting to add themselves to a vector argument:

Foo{}.addMeToVector(v);
// error: 'this' argument to member function 'addMeToVector' 
//         is an rvalue, but function has non-const lvalue ref-qualifier

as this could arguable be unexpected as compared to directly pushing back (or emplacing) temporary objects directly into the container.

If you actually want to explicitly allow moving say temporary object into the container via a member function, and for some reason do not want to directly use the push_back(T&&) overload from the vector, you could add an overload for &&-qualifiers:

struct Foo {
    int x{42};
    void addMeToVector(std::vector<Foo>& v) & {
        // copies in (push_back(const T&) overload)
        v.push_back(*this); 
    }
    void addMeToVector(std::vector<Foo>& v) && { 
        // moves in (push_back(T&&) overload)
        v.push_back(std::move(*this)); 
    }
};

This overload scheme would allow moving in temporary objects, wheres lvalues would be copied in.

You could also move in non-temporary objects, but this could lead to some lifetime confusion on the call site, as callee object (an lvalue) would move itself into the argument passed to it, and should no longer be used after that member function call, which would be quite a weird (and unexpected) semantic.


A vector of pointers to Foo objects?

[...] I needed a vector of pointers, not the object itself.

If you consider each Foo object as unique, and that ownership of it should be likewise unique (and e.g. shifted to the vector when adding Foo objects to it), you may want to re-consider if objects themselves really need to be able to add themselves to a container, and use a container of std::unique_pointer:s instead of a raw pointers. As raw pointers do not, by themselves, hold any information about ownership, they typically come with lifetime issues if not handled with care.

Related