Zyx 2000
Zyx 2000

Reputation: 1705

Keeping a vector of non-constructible objects

I have the class MyObject. All instances of it should be owned by a MyObjectSet, and it should not be possible to construct it anywhere else. Inside MyObjectSet, I use a std::vector<MyObject> to store all instances in.

The problem is, that for std::vector<MyObject> to work, the move constructor of MyObject has to be public (it is not enough to add std::vector<MyObject> as a friend of MyObject).

class MyObject {
    MyObject(int n);
    friend class MyObjectSet;
  public:
    MyObject(MyObject&&) = default; // without this, it doesn't compile
};

class MyObjectSet {
    std::vector<MyObject> MyObjects;
  public:
    MyObject& GetMyObject(int some_param);
};

But if I make it public, it would be possible to instantiate MyObject from elsewhere.

void SomeUnrelatedFunc(MyObjectSet& set) {
    MyObject m(std::move(set.GetMyObject(0))); // this shouldn't be possible
}

Is there any way to solve this problem?

It is possible to store pointers to MyObject instances inside MyObjectSet instead, but I'd like to avoid that, if possible.

Upvotes: 4

Views: 512

Answers (2)

Jeff Wofford
Jeff Wofford

Reputation: 11567

You could create a custom allocator and pass it as the second parameter to your std::vector<>. Make your custom allocator a friend of MyObject. Your custom allocator doesn't need to do anything special--the only purpose is to allocate MyObjects in the usual way (e.g. with new) while accessing the private constructor by virtue of being a friend.

Upvotes: 0

Jeff Wofford
Jeff Wofford

Reputation: 11567

You need to declare std::allocator<MyObject> as a friend:

class MyObject {

    MyObject(int n);
    friend class MyObjectSet;
    friend std::allocator<MyObject>;
};

Then use vector::emplace_back() to actually construct your objects.

class MyObjectSet {
    std::vector<MyObject> MyObjects;

public:
    MyObject& GetMyObject(int some_param)
    {
        MyObjects.emplace_back( some_param );
        return MyObjects.back();
    }
};

emplace_back() has the effect of calling the MyObject constructor, passing some_param as the int parameter to the constructor, and inserting the created object into the back of the list. The construct call is done from within std::allocator, so its friendship is required.

Upvotes: 2

Related Questions