Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::vector emplace_back implementation

Tags:

c++

vector

The push_back function that I implemented:

void push_back(T& num) {
    my_vec[index] = num;
    index++;
}

And the emplace_back function:

template<class... Args>
void emplace_back(Args&&... args) {
    push_back(T(std::forward<Args>(args)...));
}

Do you see any problem with this? If yes then could you please tell me

Also, please let me know how does this work?

Please note: the emplace_back is not my implementation, I took it from other questions as I was looking for a way to implement my own emplace_back.

like image 394
love_to_code Avatar asked Nov 15 '25 13:11

love_to_code


2 Answers

Do you see any problem with this?

You aren't really emplacing with this. There's still an assignment.

std::vector<T> doesn't allocate an array of T. It allocates raw memory with the size and alignment of an array of T, and then instantiates objects in that raw memory.

With that in mind, you should probably implement push_back in terms of emplace_back, rather than the other way around.

template <typename T>
class my_vector {
    T * start;
    std::size_t size;
    std::size_t capacity;
    using reference = T&;

    void grow(); // Implementation of this is left as an exercise to the reader

public:
    template <typename... Args>
    reference emplace_back(Args&&... args) {
        if (size == capacity) grow();
        return *new (start + size++) T(std::forward<Args>(args)...);
    }

    reference push_back(const T & t) { return emplace_back(t); }
    reference push_back(T && t) { return emplace_back(std::move(t)); }
};

Also, please let me know how does this work?

template <typename... Args> allows zero or more types to match this template, and then T(std::forward<Args>(args)...) is constructing a T with those arguments, "perfectly forwarding" them, i.e. rvalues are passed as rvalues and lvalues as lvalues.

N.b. because std::vector doesn't new[], you cannot implement something that behaves exactly like std::vector before C++ 20, because it has to be able to return a pointer to an array of T from data without constructing an array of T.

like image 177
Caleth Avatar answered Nov 18 '25 05:11

Caleth


The point of emplace_back is to construct an object in place. Your implementation constructs an object then copies it to my_vec.

Your implementation will not work with types that are not copyable. E.g. this won't compile:

Vector<std::thread> v;
v.emplace_back([](){});
v.push_back(std::thread([](){}));

Changing push_back to take it's argument via rvalue reference will fix the issue:

void push_back(T&& num) {
    my_vec[index] = std::move(num);
    index++;
}

template<class... Args>
void emplace_back(Args&&... args) {
    push_back(T(std::forward<Args>(args)...));
}

I think however that most standard library implementations are implemented using emplace_back as the lowest level function:

void push_back(T&& num) {
    emplace_back(std::move(num));
}

template<class... Args>
void emplace_back(Args&&... args) {
    my_vec[index] = T(std::forward<Args>(args)...);
    index++;
}

This then makes it easier to implement the push_back overload that copies the values:

void push_back(const T& num) {
    emplace_back(num);
}

Note that this implementation is using move assignment which is still not quite the intention of emplace_back which requires constructing an object in place using placement new on uninitialised memory but assuming my_vec is an array of objects or similar its the best you can do (without fully implementing the semantics of std::vector which is fairly complex).

like image 42
Alan Birtles Avatar answered Nov 18 '25 04:11

Alan Birtles



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!