Search code examples
c++listlinked-listundefined-behaviorsplice

Why need list argument in list splice func cpp


Why do we need a list argument in the splice func cpp? Why only iterators aren't sufficient? Result is the same if I pass l1 or l2 as a second argument l1.splice(st, l1, it, it2); or l1.splice(st, l2, it, it2);

prints 1 4 5 2 3

#include <bits/stdc++.h> 
using namespace std; 

int main() 
{ 


// initializing lists and iterator 
list<int> l1 = { 1, 2, 3 }; 
list<int> l2 = { 4, 5 }; 

auto it = l2.begin(); 
auto it2 = l2.end(); 

auto st = l1.begin();
std::advance(st,1);

// result the same if in splice l1 or l2
// 1 4 5 2 3 
l1.splice(st, l2, it, it2); 

cout << "list l1 after splice operation" << endl; 
for (auto x : l1) 
    cout << x << " "; 
return 0; 
} 

Solution

  • This call

    l1.splice(st, l1, it, it2);
    

    invokes undefined behavior.

    When you need to extract a range of elements then other data members of the list as for example size must be updated.

    If you will execute for example this statement

    std::cout << l2.size() << '\n';
    

    you can get unexpected result.

    Here is a demonstrative program compiled with gcc 8.3.

    #include <iostream>
    #include <list>
    #include <iterator>
    
    int main() 
    {
        std::list<int> lst1 = { 1, 3, 5, 7, 9 };
        std::list<int> lst2 = { 0, 2, 4, 6, 8 };
    
        lst1.splice( std::next( std::begin( lst1 ) ), 
                                lst1, 
                                std::begin( lst2 ),
                                std::end( lst2 ) );
    
        for ( const auto &item : lst1 )
        {
            std::cout << item << ' ';
        }
        std::cout << '\n';
    
        for ( const auto &item : lst2 )
        {
            std::cout << item << ' ';
        }
        std::cout << '\n';
    
        std::cout << "the size of lst2 is " << lst2.size() << '\n';
    
        return 0;
    }
    

    Its output is

    1 0 2 4 6 8 3 5 7 9 
    
    the size of lst2 is 5
    

    If you will change lst1 to lst2 in this call

        lst1.splice( std::next( std::begin( lst1 ) ), 
                                lst2,                 // <=== 
                                std::begin( lst2 ),
                                std::end( lst2 ) );
    

    then you will the correct output

    1 0 2 4 6 8 3 5 7 9 
    
    the size of lst2 is 0 
                       ^^^