3

I have the following API design problem,or rather dilemma.I seek a way to store some objects in STL vector as object.Yes,I look for a way to increase vector traversing performance through cache locality improvement. Now, the thing is, that type of object should also be exposed as non-copiable to the user of the API. And I mean implicit copy via copy constructor or assignment. But as you surely know, doing something like this:

#define PREVENT_COPY(TypeName)      \
  TypeName (const TypeName &);      \
  void operator= (const TypeName &);
#endif

class FooClass 
{
PREVENT_COPY(FooClass)
public:
  FooClass() {}
 ~FooClass() {}
};

And then:

 int main(int argc,char* argv[])
 {
     std::vector<FooClass> fooVec;
 }

Won't work, because the vector requires access to copy constructor.So ideally,I would want to enjoy both worlds,that's being able to prevent public access to copy/assignment mechanism while still being able to push objects into vector. C array can replace STL vector.But is there a hack to get it working also with std::vector in such a scenario?

As a side note: The reason why I need to disable implicit copy for that type of object is that it's created and managed by an internal system,which is responsible for the whole life cycle of objects of that type.And currently I cache those in a vector of pointers.The target are mobile platforms and the app is real time rendering system.I want to minimize dynamic allocations and improve cache locality for frequently traversed vectors of performance critical data.

Community
  • 1
  • 1
Michael IV
  • 11,016
  • 12
  • 92
  • 223

2 Answers2

4

This is exactly what rvalue references (introduced in C++11) are for. If you wish to have a single non-copyable instance that you transfer from one place to the other, you can use move-semantics along with std::vector::emplace_back.

For example:

#include <iostream>
#include <vector>

struct Foo
{
    Foo( int b ) : bar(b) {}

    Foo( const Foo& ) = delete;
    Foo& operator=( const Foo& ) = delete;

    ~Foo() = default;
    Foo( Foo&& f ) = default;
    Foo & operator=( Foo && ) = default;

    int bar;
};


int main()
{
    std::vector<Foo> foos;
    foos.emplace_back( Foo(4) );
    Foo f(2);
    foos.emplace_back( std::move( f ) );
    // Now the contents of 'f' must be considered invalid, and
    // using 'f' is undefined behavior.

    for( const Foo & f : foos )
    {
        std::cout << f.bar << std::endl;
    }
    return 0;
}

Output:

4
2

This is a naive example because it's using trivial data types and defaulted move-construction. But hopefully you get the picture.

paddy
  • 60,864
  • 6
  • 61
  • 103
  • I should point out that there is also `std::vector::emplace` which is the move-equivalent of `std::vector::insert`. – paddy Mar 23 '17 at 10:07
  • `using 'f' is undefined behavior. ` this isn't true. Moved from object are in a valid but unspecified state in most cases. – Guillaume Racicot Mar 23 '17 at 13:32
  • If the state of `f` is unspecified, then logic depending on an assumed state is undefined. – paddy Mar 23 '17 at 21:52
0

The elements of a vector are stored contiguously in the memory. The thing you want to achieve is push separate objects placed in memory into a vector. You can move an object into a vector using std::vector::emplace_back by using its move constructor (by default). However all pointers pointing at that object will be invalid.

In my opinion using a pool like structure and creating your objects beforehand is a better approach in your case.

qua
  • 33
  • 1
  • 5
  • Thought of that.A pool would be extremely inefficient in my case.The amount of objects is quite dynamic (think of game engine with varying amount of objects in a scene)and also I really care about the whole process memory footprint during runtime.Preallocacting a huge pool of those objects would be a real waste of memory space. – Michael IV Mar 23 '17 at 10:19
  • Have you considered using a component based entity model? You can pool a block of component objects and reuse them whenever needed. – qua Mar 23 '17 at 10:26
  • I do use component based paradigm.Still,one should be careful with mem pools ;) – Michael IV Mar 23 '17 at 10:28
  • 1
    I disagree with the statements made in this answer. (1) No, you _can_ do that. (2) By setting the vector size correctly to begin with, you will not have pointers to those objects become invalid... and in any case, that was not a requirement of the question. (3) Still does not solve the problem of transferring an object's data from one place to another, unless maybe you start abusing placement-new. – paddy Mar 23 '17 at 10:31
  • *The reason why I need to disable implicit copy for that type of object is that it's created and managed by an internal system,which is responsible for the whole life cycle of objects of that type.*. I was considering these objects (wanted to be placed in a vector) were created and are being used in some other systems. By *moving* these objects you may actually invalidate references are used in other systems. – qua Mar 23 '17 at 10:43
  • That's fair enough. I would argue that allowing references to these objects in the first place breaks the design if the API user does not understand the underlying container. If you wanted to allow referencing directly into a container that could also grow, and also guarantee that existing references would not be invalidated, then you would use `std::deque` as the container instead. And all other points I made still hold. – paddy Mar 23 '17 at 10:47