Home | Projects | Notes > C++ Programming > Sequence Container - std::list, std::forward_list
std::list, std::forward_list
Sequence containers.
Non-contiguous in memory.
No direct access to elements using [] or at().
Very efficient for inserting and deleting elements once an element is found.
std::list

Declared in the <list> header file.
Dynamic size
Doubly-linked lists of elements.
Supports bi-directional traverse.
Direct element access is NOT provided.
Rapid insertion and deletion of elements anywhere in the container - Constant time: O(1)
If you need a container where you'll have lots of insertions and deletions from the container and you don't need direct access to the elements, then the list is a good choice.
All iterators are available but may become invalid when the corresponding element is deleted.
x
1// Initialization2std::list<int> l1{1, 2, 3, 4, 5};3std::list<int> l2(10, 100); // ten 100s - overloaded constructor4
5// Initialization6std::list<std::string> l3{7    std::string{"Kyungjae"},8    "Sunny",    // C-style string will be converted to a std::string.9    std::string{"Yena"}10};11
12// Assignment via initializer list13l1 = {2, 4, 6, 8, 10};For more information, see cppreference.com.
341std::list<int> l1{1, 2, 3, 4, 5};2std::list<int> l2{10, 20, 30, 40, 50};3
4std::cout << l1.size();     // 55std::cout << l1.max_size(); // A very large number6std::cout << l1.front();    // 1 (Returns reference to the first element)7std::cout << l1.back();     // 5 (Returns reference to the last element)8std::cout << l1.empty();    // 0 (false)9
10// The following won't work since std::sort() requires random access11// iterators, and std::list provides only bidirectional iterators.12//std::sort(l1.begin(), l1.end());13
14l1.sort();15
16auto it = std::find(l1.begin(), l1.end(), 3);17l1.insert(it, 10);  // 1, 2, 10, 3, 4, 518l1.erase(it);       // erase 3. now 1, 2, 10, 4, 5 ('it' becomes invalid)       19
20it = std::find(l1.begin(), l1.end(), 4);21l1.insert(it, l2.begin(), l2.end());22    // 1, 2, 10, 10, 20, 30, 40, 50, 4, 523
24// Lists are bi-directional, so iterators can iterate in both directions.25std::cout << *it;   // 426it++;27std::cout << *it;   // 528it--;29std::cout << *it;   // 430
31l1.resize(2);       // 1, 232l1.resize(5);       // 1, 2, 0, 0, 033
34l1.swap(l2);                // Swaps the two dequesL17: The list will insert an element very efficiently before 3 and the
itwill still reference 3.
The std::list allows for efficiently inserting and deleting elements at the front and back:
91person p{"Kyungjae", 30};2std::list<person> d;3
4l.push_back(p);                 // Add p to the back (copy is made)5l.pop_back();                   // Remove the last element (p in this case)6l.push_front(person{"Sunny", 20});7l.pop_front();                  // Remove element from the front8l.emplace_front("Yena", 5);     // Construct the object in-place. Very efficient!9l.emplace_back("Sunny", 20);    // Construct the object in-place. Very efficient!
std::forward_list (C++11)

Declared in the <forward_list> header file. Introduced in C++11.
Dynamic size
Singly-linked lists of elements.
Supports uni-directional traverse only. (Does not have the concept of "back".)
Less overhead than a std::list.
Direct element access is NOT provided.
Rapid insertion and deletion of elements anywhere in the container - Constant time: O(1)
If you need a container where you'll have lots of insertions and deletions from the container and you don't need direct access to the elements, then the list is a good choice.
Reverse iterators are not available.
Iterators may become invalid when the corresponding element is deleted.
131// Initialization2std::deque<int> d1{1, 2, 3, 4, 5};3std::deque<int> d2(10, 100);    // ten 100s4
5// Initialization6std::deque<std::string> d3{7    std::string{"Kyungjae"},8    "Sunny",    // C-style string will be converted to a std::string.9    std::string{"Yena"}10};11
12// Assignment via initializer list13d1 = {2, 4, 6, 8, 10};For more information, see cppreference.com.
341std::list<int> l1{1, 2, 3, 4, 5};2std::list<int> l2{10, 20, 30, 40, 50};3
4//std::cout << l1.size();       // size() not supported5std::cout << l1.max_size(); // A very large number6std::cout << l1.front();    // 1 (Returns reference to the first element)7//std::cout << l1.back();       // back() not supported8std::cout << l1.empty();    // 0 (false)9
10// The following won't work since std::sort() requires random access11// iterators, and std::list provides only bidirectional iterators.12//std::sort(l1.begin(), l1.end());13
14l1.sort();15
16auto it = std::find(l1.begin(), l1.end(), 3);17l1.insert(it, 10);  // 1, 2, 10, 3, 4, 518l1.erase(it);       // erase 3. now 1, 2, 10, 4, 5 ('it' becomes invalid)       19
20it = std::find(l1.begin(), l1.end(), 4);21l1.insert_after(it, 10);    // 1, 2, 3, 10, 4, 10, 522l1.emplace_after(it, 100);  // 1, 2, 3, 10, 4, 100, 10, 523l1.erase_after(it);         // Erase the 100. 1, 2, 3, 10, 4, 10, 524
25// Lists are bi-directional, so iterators can iterate in both directions.26std::cout << *it;   // 427it++;28std::cout << *it;   // 529//it--;             // Not supported30
31l1.resize(2);       // 1, 232l1.resize(5);       // 1, 2, 0, 0, 033
34l1.swap(l2);                // Swaps the two deques
max_size(): Returns the maximum number of elements a vector can theoretically hold on the currentsystem. This is typically a very large number and depends on system and implementation limits.
91person p{"Kyungjae", 30};2std::deque<person> d;3
4//d.push_back(p);                   // Not supported5//d.pop_back();                     // Not supported6d.push_front(person{"Sunny", 20});7d.pop_front();                  // Remove element from the front8d.emplace_front("Yena", 5);     // Construct the object in-place. Very efficient!9//d.emplace_back("Sunny", 20);      // Not supportedL6: Creates a temporary (unnamed) person object and adds it to the forward list using move semantics.
L8: Constructs the person object directly in place using the constructor. Very efficient - no moves, no copies. It’s built exactly where it needs to be. Use this!
std::listEnsure that your custom classes provide the following three elements to work correctly with the STL:
Overloaded default constructor
Overloaded operator<
Overloaded operator==
test3() in the following code demonstrates the importance of defining a default constructor for your custom class.
1911// std::advance()5
6class person7{8public:9    person() : name{"Unkown"}, age{0} {}    // will be useful when resizing happens10    person(std::string name, int age)11        : name{name}, age{age}12    {}13    bool operator<(const person &rhs) const14    {15        return this->age < rhs.age;16    }17    bool operator==(const person &rhs) const 18    {19        return (this->name == rhs.name && this->age == rhs.age);20    }21    22private:23    friend std::ostream& operator<<(std::ostream &os, const person &p);24    std::string name;25    int age;26};27
28// Overloading the insertion operator as a global function.29std::ostream& operator<<(std::ostream &os, const person &p)30{31    os << p.name << ":" << p.age;32    return os;33}34
35// Template function to print elements of any list.36template <typename T>37void print(const std::list<T> &l)38{39    std::cout << "[ ";40    for (const auto &elem : l)41    {42        std::cout << elem << " ";43    }44    std::cout << "]" << std::endl;45}46
47// push_front(), push_back()48void test1(void)49{50    std::cout << "\nTEST1" << std::endl;51
52    std::list<int> l1{1, 2, 3, 4, 5};53    print(l1);54
55    // Very efficient when inserting at both ends.56    std::list<std::string> l2;57    l2.push_back("Back");58    l2.push_front("Front");59    print(l2);60
61    std::list<int> l3;62    l3 = {1, 2, 3, 4, 5, 5, 6, 7, 8, 9, 10};63    print(l3);64
65    std::list<int> l4(10, 100); // ten 100s in the list (using constructor)66    print(l4);67}68
69// size(), front(), back(), clear()70void test2(void)71{72    std::cout << "\nTEST2" << std::endl;73
74    std::list<int> l{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};75    print(l);76
77    std::cout << "Size: " << l.size() << std::endl;78
79    std::cout << "Front: " << l.front() << std::endl;80    std::cout << "Back: " << l.back() << std::endl;81
82    l.clear();83    print(l);84    std::cout << "Size: " << l.size() << std::endl;85}86
87void test3(void)88{89    std::cout << "\nTEST3" << std::endl;90
91    std::list<int> l1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};92    print(l1);93
94    l1.resize(5);95    print(l1);96
97    l1.resize(10);  98    print(l1);99
100    // This shows why it is important to define a overloaded default constructor101    // when using a custom class with the STL. (as well as 'operator<' and 102    // 'operator==')103    std::list<person> l2;104    l2.resize(5);   // uses the person's overloaded default constructor105    print(l2);106}107
108void test4(void)109{110    std::cout << "\nTEST4" << std::endl;111
112    std::list<int> l1{1, 2, 3, 4, 5, 6, 7, 8, 9, 10};113    print(l1);114    auto it = std::find(l1.begin(), l1.end(), 5);115    if (it != l1.end())116    {117        l1.insert(it, 100); // insert a 100 before the iterator118    }119    print(l1);120
121    std::list<int> l2{1000, 2000, 3000};122    l2.insert(it, l2.begin(), l2.end());    // 'it' still referencing 5 in l1123    print(l1);  // 1 2 3 4 100 1000 2000 3000 5 6 7 8 9 10124
125    std::advance(it, -4);   // point to the 100126    std::cout << *it << std::endl;127
128    l1.erase(it);   // remove the 100 - iterator becomes invalid129                    // to use iterator again you need to reset it!130    print(l1);131}132
133// emplace(), emplace_back(), find()134void test5(void)135{136    std::cout << "\nTEST5" << std::endl;137
138    std::list<person> l{139        {"Kyungjae", 30},140        {"Sunny", 20},141        {"Yena", 5}142    };143
144    print(l);145    std::string name;146    int age{};147    std::cout << "\nEnter the name of the next person: ";148    getline(std::cin, name);149    std::cout << "Enter their age: ";150    std::cin >> age;151
152    // I'm not creating an object to add to the list. The list will create an153    // object for me based on the passed values.154    l.emplace_back(name, age);155    print(l);156
157    // Insert Nina before Yena158    auto it = std::find(l.begin(), l.end(), person{"Yena", 5});159    if (it != l.end())160    {161        l.emplace(it, "Nina", 1);162    }163    print(l);164}165
166// sort()167void test6(void)168{169    std::cout << "\nTEST6" << std::endl;170
171    std::list<person> l{172        {"Kyungjae", 30},173        {"Sunny", 20},174        {"Yena", 5}175    };176
177    print(l);178    l.sort();   // sort() will use the overloaded 'operator<' 179    print(l);180}181
182int main(int argc, char *argv[])183{184    test1();185    test2();186    test3();187    test4();188    test5();189    test6();190    return 0;191}381TEST12[ 1 2 3 4 5 ]3[ Front Back ]4[ 1 2 3 4 5 5 6 7 8 9 10 ]5[ 100 100 100 100 100 100 100 100 100 100 ]6
7TEST28[ 1 2 3 4 5 6 7 8 9 10 ]9Size: 1010Front: 111Back: 1012[ ]13Size: 014
15TEST316[ 1 2 3 4 5 6 7 8 9 10 ]17[ 1 2 3 4 5 ]18[ 1 2 3 4 5 0 0 0 0 0 ]19[ Unkown:0 Unkown:0 Unkown:0 Unkown:0 Unkown:0 ]20
21TEST422[ 1 2 3 4 5 6 7 8 9 10 ]23[ 1 2 3 4 100 5 6 7 8 9 10 ]24[ 1 2 3 4 100 1000 2000 3000 5 6 7 8 9 10 ]2510026[ 1 2 3 4 1000 2000 3000 5 6 7 8 9 10 ]27
28TEST529[ Kyungjae:30 Sunny:20 Yena:5 ]30
31Enter the name of the next person: Miyoung32Enter their age: 5033[ Kyungjae:30 Sunny:20 Yena:5 Miyoung:50 ]34[ Kyungjae:30 Sunny:20 Nina:1 Yena:5 Miyoung:50 ]35
36TEST637[ Kyungjae:30 Sunny:20 Yena:5 ]38[ Yena:5 Sunny:20 Kyungjae:30 ]