0

I was fiddling with various ways to handle data when I came across the following behavior that I don't understand. In reading the code below please pay attention to the variables firstDoubleIter and secondDoubleIter, in particular which elements of the (various copies) of the list they point to.

My question is below.

#include <list>                                                                      
#include <iostream>                                                                  

template <class T>                                                                                            
class MyListContainer                                                                
{                                                                                    
  public:                                                                            
    std::list<T> list;                                                               
    MyListContainer() : list() {;}                                                   
    MyListContainer(const MyListContainer& container) : list(container.list) {;}     
    MyListContainer(std::list<T> list) : list(list) {;}                              
    std::list<T> getList() const {return list;}                                      
};                                                                                   

int main()                                                                           
{                                                                                    
  typedef MyListContainer<double> DoubleList;                                        
  DoubleList doubleContainer = DoubleList(std::list<double>({0, 1}));                
  auto firstDoubleIter = doubleContainer.getList().begin();                          
  auto secondDoubleIter = ++(doubleContainer.getList().begin());                     
  std::cout << "Using the getList() method\n";                                       
  std::cout << "*firstDoubleIter = " << *firstDoubleIter << "\n";                    
  std::cout << "*secondDoubleIter = " << *secondDoubleIter << "\n";                  

  std::list<double> listOfDoubles = doubleContainer.list;                            
  firstDoubleIter = listOfDoubles.begin();                                           
  secondDoubleIter = ++listOfDoubles.begin();                                        
  std::cout << "Accessing the list directly\n";                                      
  std::cout << "*firstDoubleIter = " << *firstDoubleIter << "\n";                    
  std::cout << "*secondDoubleIter = " << *secondDoubleIter << "\n";                  
}

Which produces the following output:

Using the getList() method
*firstDoubleIter = 1
*secondDoubleIter = 0
Accessing the list directly
*firstDoubleIter = 0
*secondDoubleIter = 1

My understanding is that the values for *firstDoubleIter and *secondDoubleIter should be the same even with the implicit list copy.

My question is: Why are they not the same?

Jaffa
  • 12,442
  • 4
  • 49
  • 101
Ben Whale
  • 493
  • 2
  • 9

1 Answers1

4

In the first case, each call to getList returns a temporary copy of the list, destroyed at the end of the statement. Since the iterators no longer refer to valid list elements, you get undefined behaviour when you dereference them.

In the second case, you store a local copy of the list, so the iterators are still valid.

Depending on how you intend to use this class, you probably want getList to return a reference rather than a copy.

Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
  • Ah... so the scope of the object returned by getList() is only the line on which the call is made. In order to ensure that the object returned persists it needs to be assigned to something. Because iterators don't copy the objects that they iterator over there is no assignment occurring in the first case? – Ben Whale Nov 12 '13 at 09:58
  • @BenWhale: That's right. Most iterators are like generalised pointers - it refers to an object in a sequence, but doesn't influence the object's lifetime, and can be left dangling if something else destroys the object. – Mike Seymour Nov 12 '13 at 10:03
  • Great! Thanks for the taking the time to explain what is a silly mistake. – Ben Whale Nov 12 '13 at 10:11