Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

vector::emplace_back for objects with a private constructor

I want my Timer objects to be created via Timer::create() only. For this purpose, I made the constructor private. However, I get a compiler error saying that "Timer::Timer(unsigned int)' is private" within the context of new_allocator.h. How can I solve this problem?

class Timer {
    private:
        int timeLeft;
        Timer(unsigned int ms) : timeLeft(ms) {}

    public:
        static std::vector<Timer> instances;
        static void create(unsigned int ms) {
            instances.emplace_back(ms);
        }
};

std::vector<Timer> Timer::instances;
like image 885
A.B. Avatar asked Jun 09 '13 08:06

A.B.


People also ask

Does Emplace_back call constructor?

So you can emplace_back does use the desired constructor to create the element and call copy constructor when it need to grow the storage. You can call reserve with enough capacity upfront to avoid the need to call copy constructor.

Does Emplace_back use move constructor?

Calling emplace_back will call the move constructor of std::string when std::move is used, which could save on a copy (so long as that string isn't stored in a SSO buffer). Note that this is essentially the same as push_back in this case.

What is Emplace_back in vector?

C++ Vector Library - emplace_back() Function The C++ function std::vector::emplace_back() inserts new element at the end of vector. Reallocation happens if there is need of more space. This method increases container size by one.

Can you create an object if a constructor is private C++?

You can't instantiate your class because the constructor is private . private member variables and functions cannot be used outside of the class itself.


2 Answers

You can use transfer-of-friendship semantics to avoid having to have a specialized vector allocator. It's a bit like dependency injection of friendship. This is really quite simple. You create an empty class who makes itself a friend of your class. But the default constructor is private, so only your class can create instances of it. But the class is still copyable, so it can be passed to anybody.

Your Timer constructor will be public, but it requires one of these objects as an argument. Therefore, only your class, or a function called by it, can create these objects directly (copies/moves will still work).

Here's how you could do that in your code (live example):

class TimerFriend
{
public:
  TimerFriend(const TimerFriend&) = default;
  TimerFriend& operator =(const TimerFriend&) = default;

private:
  TimerFriend() {}

  friend class Timer;
}

class Timer {
    private:
        int timeLeft;

    public:
        Timer(unsigned int ms, const TimerFriend&) : timeLeft(ms) {}

        static std::vector<Timer> instances;
        static void create(unsigned int ms) {
            instances.emplace_back(ms, TimerFriend());
        }
};

std::vector<Timer> Timer::instances;
like image 148
Nicol Bolas Avatar answered Nov 15 '22 12:11

Nicol Bolas


You probably should implement your own allocator, that will be friend to timer:

class Timer {

    struct TimerAllocator: std::allocator<Timer>
    {
        template< class U, class... Args >
        void construct( U* p, Args&&... args )
        {
            ::new((void *)p) U(std::forward<Args>(args)...);
        }

        template< class U > struct rebind { typedef TimerAllocator other; };

    };
    friend class TimerAllocator;

    private:
        int timeLeft;

    Timer(unsigned int ms) : timeLeft(ms) 
    {}

    public:
        static std::vector<Timer, TimerAllocator> instances;
        static void create(unsigned int ms) {
            instances.emplace_back(ms);
        }
};

std::vector<Timer, Timer::TimerAllocator> Timer::instances;

int main()

{
    Timer::create(100);
}

The simplest solution would be derive from std::allocator<Timer> reimplementing rebind to rebind to itself, so vector couldn't rebind allocator back to std::allocator and implement own construct to actually create Timers.

like image 34
Lol4t0 Avatar answered Nov 15 '22 12:11

Lol4t0