emplace_back


Method: emplace_back

emplace_back is a powerful member function of C++ container classes, introduced in C++11. It constructs an element in-place at the end of the container, avoiding unnecessary copies or moves. This function is particularly useful when dealing with complex objects or when performance is critical. Let's explore various examples of emplace_back usage in different scenarios.

Example 1: Basic Usage with Simple Types

#include <iostream>
#include <vector>

int main() {
    std::vector<int> numbers;

    numbers.emplace_back(42);
    numbers.emplace_back(100);

    for (const auto& num : numbers) {
        std::cout << num << " ";
    }
    std::cout << std::endl;

    return 0;
}

Explanation

In this basic example, we use emplace_back to add integers to a vector of ints. While for simple types like int, there isn't much difference between push_back and emplace_back, it's a good starting point to understand the syntax.

Example 2: Using emplace_back with Custom Classes

#include <iostream>
#include <vector>
#include <string>

class Person {
public:
    Person(std::string name, int age) : name_(std::move(name)), age_(age) {
        std::cout << "Constructing Person: " << name_ << std::endl;
    }

    Person(const Person& other) : name_(other.name_), age_(other.age_) {
        std::cout << "Copying Person: " << name_ << std::endl;
    }

    Person(Person&& other) noexcept : name_(std::move(other.name_)), age_(other.age_) {
        std::cout << "Moving Person: " << name_ << std::endl;
    }

    friend std::ostream& operator<<(std::ostream& os, const Person& p) {
        return os << p.name_ << " (" << p.age_ << ")";
    }

private:
    std::string name_;
    int age_;
};

int main() {
    std::vector<Person> people;

    std::cout << "Using emplace_back:" << std::endl;
    people.emplace_back("Alice", 30);

    std::cout << "\nUsing push_back:" << std::endl;
    Person bob("Bob", 25);
    people.push_back(bob);

    std::cout << "\nPeople in the vector:" << std::endl;
    for (const auto& person : people) {
        std::cout << person << std::endl;
    }

    return 0;
}

Explanation

Example 3: emplace_back with Multiple Arguments

#include <iostream>
#include <vector>
#include <string>

struct ComplexObject {
    std::string name;
    int id;
    double value;

    ComplexObject(std::string n, int i, double v) 
        : name(std::move(n)), id(i), value(v) {
        std::cout << "Constructing ComplexObject: " << name << std::endl;
    }

    friend std::ostream& operator<<(std::ostream& os, const ComplexObject& obj) {
        return os << obj.name << " (ID: " << obj.id << ", Value: " << obj.value << ")";
    }
};

int main() {
    std::vector<ComplexObject> objects;

    objects.emplace_back("Object1", 1, 3.14);
    objects.emplace_back("Object2", 2, 2.718);

    for (const auto& obj : objects) {
        std::cout << obj << std::endl;
    }

    return 0;
}

Explanation

Example 4: emplace_back with Initializer Lists

#include <iostream>
#include <vector>
#include <string>

struct Data {
    std::vector<int> values;

    Data(std::initializer_list<int> list) : values(list) {
        std::cout << "Constructing Data with " << values.size() << " elements" << std::endl;
    }

    friend std::ostream& operator<<(std::ostream& os, const Data& data) {
        os << "Data: ";
        for (int val : data.values) {
            os << val << " ";
        }
        return os;
    }
};

int main() {
    std::vector<Data> dataVector;

    // Correct usage of emplace_back with initializer lists
    dataVector.emplace_back(std::initializer_list<int>{1, 2, 3, 4, 5});
    dataVector.emplace_back(std::initializer_list<int>{10, 20, 30});

    // Alternative approach using push_back
    dataVector.push_back({100, 200, 300});

    for (const auto& data : dataVector) {
        std::cout << data << std::endl;
    }

    return 0;
}

Explanation

Additional Note on emplace_back vs push_back with Initializer Lists

It's worth noting that for cases involving initializer lists, push_back can sometimes be more straightforward to use than emplace_back. This is because push_back has special handling for initializer lists, allowing direct use of braced initializers.

When using emplace_back with initializer lists, you need to be explicit about the type, as shown in the corrected example. This can make push_back a more convenient choice in such scenarios, especially when the initializer list type is clear from the context.

Summary

emplace_back is a powerful feature in modern C++ that allows for efficient in-place construction of elements in containers. Key points to remember:

By using emplace_back, developers can write more efficient code, particularly when working with containers of complex objects. It's an essential tool in the modern C++ developer's toolkit for optimizing container operations.

Related

Previous Page | Course Schedule | Course Content