"List iterator not dereferencable"

Hi guys,
I'm trying to make a program that reads a file, containing the first and last name of a set of candidates, then stores that information in a STL list, then print it.

I have made a similar program using arrays instead of lists and it has worked, however whenever I try to output an element of my list (of objects) I get an error:
"list iterator not dereferencable"

I have tried making this program take user input instead of reading from a file and it has worked.

However I am still unsure of why I am receiving an error for my current code (I have little experience with STL).

Can anyone please explain the reason behind the error? Thank you for your assistance.



1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
  //HEADER
#include <iostream>
#include <list>
#include <string>
#include <iterator>

using namespace std;

class candidateType
{
public:
	friend istream& operator >> (istream&, candidateType&);
	//overload insertion operator

	friend ostream& operator<<(ostream&, const candidateType&);
	//overload output operator
	candidateType(string f = " ", string l = " ");


	
private:
	//variable for f name
	string fn;
	//varaible for l name
	string ln;
	
};

//CPP
candidateType::candidateType(string f, string l)
{
	fn = f;
	ln = l;
}



istream& operator>>(istream& isObj, candidateType& candObj)
{
	isObj >> candObj.fn >> candObj.ln;
	return isObj;
}

ostream& operator<<(ostream& osObj, const candidateType& candObj)
{
	osObj << candObj.fn << " " << candObj.ln;
	return osObj;
}


//MAIN
#include <fstream>
#include "candidateType.h"

void getData(ifstream& file, list<candidateType>& c)
{
	string f, l;
	for (int i = 0; i < c.size(); i++)
	{
		file >> f >> l;
		candidateType obj(f, l);
		
		c.push_back(obj);
	}
}

int main()
{
	list<candidateType> candidates;
	ifstream ifile;
	
	ifile.open("data.txt");
	if (!ifile)
		cout << "Could not open file!\n";
	else
		getData(ifile, candidates);
	
	
	cout << candidates.front(); 


    return 0;
}



//FIlE CONTAINING INFORMATION (CANDIDATES)
Gordon Lee 
Lucy Collins  
Jimmy Lowe  
Dave Tran  
You pass an empty list to getData, so the for loop doesn't get executed and in main your try to access the first element of an empty list which crashes the app.

Your getData function should rather look like this:
1
2
3
4
5
6
7
8
void getData(ifstream& file, list<candidateType>& c)
{
  candidateType ct;
  while (file >> ct)
  {
    c.push_back(ct);
  }
}
The problem is line 58. Upon entry the size of the list is zero, so the loop never executes.

A better way to read the names is:
1
2
3
4
    while (file >> f >> l) {
        candidateType obj(f, l);
        c.push_back(obj);
    }

file >> f >> l extracts two strings. The result of this expression is a reference to the istream
while() expects a bool, so the compiler looks to see if istream & can be converted to bool. istream provides the conversion, which returns true of the stream is good. So this basically says while (you successfully extract 2 strings). Pretty cool huh?

Returning to your original problem. It's just blind luck that the list was empty when you called getData(). Your code will be more robust if you clear the list before populating it. So the final function is:
1
2
3
4
5
6
7
8
9
10
void
getData(ifstream & file, list < candidateType > &c)
{
    string f, l;
    c.clear();
    while (file >> f >> l) {
        candidateType obj(f, l);
        c.push_back(obj);
    }
}

Thomas and Dave have already pin-pointed the issues, in addition I'd like to add something re the input, output operator overloads for custom datatypes: passing and returning std::ostream (for output) and std::istream (for input)
... applies only to streams with the character type char. If the function is intended only for use in Western Europe or in North America, this is no problem. On the other hand, a more general version requires only a little extra work, so it should at least be considered.
- “The C++ Standard Library, 2nd edition” by N Josuttis, Chapter 15: Input/Output Using Stream Classes
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
# include <iostream>
# include <list>
# include <string>
# include <fstream>
# include <sstream>

//using namespace std;
//https://stackoverflow.com/questions/1452721/why-is-using-namespace-std-considered-bad-practice

class candidateType
{
    public:
        template <typename charT, typename traits> //overload insertion operator
        friend inline std::basic_istream <charT, traits>&
            operator >> (std::basic_istream <charT, traits> & inFile, candidateType& c);

        template <typename charT, typename traits>  //overload output operator
        friend inline std::basic_ostream <charT, traits>&
            operator << (std::basic_ostream <charT, traits> & outFile, const candidateType& c);

        candidateType(const std::string& firstName , const std::string& lastName)
        : m_firstName(firstName), m_lastName(lastName){} //member initialization

    private:
        std::string m_firstName;
        std::string m_lastName;
	//give your variables meaningful names rather than unnecessary comments
};

template <typename charT, typename traits>
inline std::basic_istream <charT, traits>&
    operator >>(std::basic_istream<charT, traits> & inFile, candidateType& c)
{
	inFile >> c.m_firstName >> c.m_lastName;
	return inFile;
}

template <typename charT, typename traits>
inline std::basic_ostream <charT, traits>&
            operator << (std::basic_ostream <charT, traits> & outFile, const candidateType& c)
{
	outFile << c.m_firstName << " " << c.m_lastName;
	return outFile;
}

template <typename charT, typename traits>
void getData(std::basic_istream<charT, traits>& inFile, std::list<candidateType>& c)
{
	if(inFile)
    {
        std::string line;
        while (getline(inFile, line))
        {
            std::istringstream stream{line};
            std::string firstName, lastName;
            if(stream)stream >> firstName >> lastName;
            if(inFile)
            {
                c.emplace_front(candidateType(firstName, lastName));
            }
        }
    }
    else
    {
        std::cout << "could not open file \n";
    }//let getData() handle file non-openings
}
int main()
{
	std::list <candidateType> candidates;
	std::ifstream inFile{"C:\\test.txt"};
	//std::ifstream object can be initialized directly
	//the file is opened when it's associated ifstream object is initialized and 
       //closed, if still open, when the object goes out of scope 

    getData(inFile, candidates);

    for (const auto& elem : candidates)std::cout << elem << "\n";
}
Last edited on
With
1
2
3
4
5
6
class candidateType
{
         // ...
        std::string m_firstName;
        std::string m_lastName;
};

Writing these templates in this manner is laughably asinine.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
template <typename charT, typename traits>
inline std::basic_istream <charT, traits>&
    operator >>(std::basic_istream<charT, traits> & inFile, candidateType& c)
{
	inFile >> c.m_firstName >> c.m_lastName; // vacuous
	return inFile;
}

template <typename charT, typename traits>
inline std::basic_ostream <charT, traits>&
            operator << (std::basic_ostream <charT, traits> & outFile, const candidateType& c)
{
	outFile << c.m_firstName << " " << c.m_lastName; // daft
	return outFile;
}

template <typename charT, typename traits>
void getData(std::basic_istream<charT, traits>& inFile, std::list<candidateType>& c)
{
	if(inFile)
    {
        std::string line;
        while (getline(inFile, line)) // dumb
// ... 

ah! the 'great' JLBorges' latest tirade - laughably immature emotionally

what i find amusing is that no reason is offered for the various gratuitous abuse hurled at me while I've quote what Josuttis recommends and tried to proceed on those line - once again shows how the 'great' JLBorges operates - things are "asinine/vacuous/daft/dumb" simply because s/he says so

previous episodes: http://www.cplusplus.com/forum/beginner/218686/2/#msg1008548
JLBorges wrote:
Writing these templates in this manner is [poor design]

Why's that? It looks okay to me.

If you hadn't been on this forum for years, I would have just assumed you were a troll. Don't flame. It's not useful.
Last edited on
Why's that? It looks okay to me.

@mbozzi,
I am quite a newbie with templates so I have trouble understanding it.
If you have candidateType with a normal string and default traits, why would you use a stream with different (special) traits? Not sure if / how two different traits work together.

@gunnerfunner, could you maybe enlighten me?
Last edited on
> Writing these templates in this manner is [poor design]

>> Why's that? It looks okay to me.

Try instantiating any of these templates with any stream where the char_type is not char and/or the traits_type is not std::char_traits<char> and you will be enlightened.

For instance: http://coliru.stacked-crooked.com/a/ce05cf03ba935918


> If you have candidateType with a normal string and default traits, why would you
> use a stream with different (special) traits? Not sure if / how two different traits work together.

Obviously they won't work together.


> If you hadn't been on this forum for years, I would have just assumed you were a troll.

Thank you very much!

Tip: to test/understand template code, it is a good idea to try and instantiate the template with at least two different sets of template arguments. The compiler might know something that you are not aware of.
If you hadn't been on this forum for years, I would have just assumed you were a troll. Don't flame. It's not useful.

mbozzi: it was kind of you to add for voice to this matter, thanks
If it is required that the class must be input and output streamable for all possible stream types (in general it is not required; insisting, or even suggesting, that this must be done for each and every streamable class is asinine):

One sensible way way to do it is to follow the model shown by he standard library: parameterise the class on the character type and its associated traits type. For instance std::basic_string<C,T,A> is streamable only where the stream type is std::basic_istream<C,T> or std::basic_ostream<C,T>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <iostream>
#include <string>
#include <vector>
#include <list>
#include <sstream>
#include <fstream>

template < typename CHAR_TYPE, typename TRAITS_TYPE = std::char_traits<CHAR_TYPE> >
class candidate
{
    public:

        using string_type = std::basic_string<CHAR_TYPE,TRAITS_TYPE> ;
        using istream_type = std::basic_istream<CHAR_TYPE,TRAITS_TYPE> ;
        using ostream_type = std::basic_ostream<CHAR_TYPE,TRAITS_TYPE> ;

        candidate() = default ;
        candidate( string_type first_name, string_type last_name )
           : first_name( std::move(first_name) ), last_name( std::move(last_name) ) {}

        // ...

    private:
        string_type first_name;
        string_type last_name;

        friend istream_type& operator>> ( istream_type& stm, candidate& c )
        {
            string_type fname, lname ;
            if( stm >> fname >> lname ) c = { fname, lname } ;
            else c = {} ;
            return stm ;
        }

        friend ostream_type& operator<< ( ostream_type& stm, const candidate& c )
        { return stm << c.first_name << stm.widen('\t') << c.last_name ; }
};

template < typename CHAR_TYPE, typename TRAITS_TYPE, typename ALLOCATOR_TYPE,
           template <typename,typename> class SEQ_CONTAINER_TYPE >
typename candidate<CHAR_TYPE,TRAITS_TYPE>::istream_type&
get_candidates( typename candidate<CHAR_TYPE,TRAITS_TYPE>::istream_type& stm,
                SEQ_CONTAINER_TYPE< candidate<CHAR_TYPE,TRAITS_TYPE>, ALLOCATOR_TYPE >& cntr )
{
    cntr.clear() ;
    typename candidate<CHAR_TYPE,TRAITS_TYPE>::string_type line ;
    while( std::getline( stm, line ) )
    {
        std::basic_istringstream<CHAR_TYPE,TRAITS_TYPE> str_stm(line) ;
        candidate<CHAR_TYPE,TRAITS_TYPE> c ;
        if( str_stm >> c ) cntr.push_back( std::move(c) ) ;
        else { stm.clear( stm.failbit ) ; return stm ; }
    }

    return stm ;
}

int main()
{
    {
        std::vector< candidate<char> > vec ;
        std::ifstream file( "my_file.narrow.txt" ) ;
        get_candidates( file, vec ) ;
        for( const auto& c : vec ) std::cout << c << '\n' ;
    }

    {
        std::list< candidate<wchar_t> > lst ;
        std::wifstream wfile( "my_file.wide.txt" ) ;
        get_candidates( wfile, lst ) ;
        for( const auto& c : lst ) std::wcout << c << L'\n' ;
    }

    // etc.
}

Note that we can elide the call to stm.widen(); it is there only to emphasise that when dealing with streams, a good programmer must always be aware of the character type that the stream deals with.

The other sane option (which is more involved) is to implement the class to use either UTF-8 encoded narrow character strings or wide character strings and then perform conversions back and forth between the character type and traits used by the stream and those that are used by the class.

In either case, it is an elementary mistake to assume that the code would work correctly for all possible character typs, encodings and traits, because it appeared to work correctly when the character type was plain char with default character traits.
... be done for each and every streamable class is asinine):

ah! here we go again, the 'great' JLBorges' favorite insult ... what an emotionally immature person!

previous episodes of his/her continued trolling: http://www.cplusplus.com/forum/beginner/218686/2/#msg1008548
Last edited on
Obviously they won't work together.

Ah, I didn't notice the conflict.

I don't know what to say further aside from requesting that you offer a direct reason (even just a note) for why something is bad, e.g. "this expression ... won't compile" or "there's a bug on line x". I was annoyed at the lack of such a reason - the one you provided was implied (subtly or otherwise), and I didn't catch it, so the fault is mostly mine. My apologies.

To test/understand template code, it is a good idea to try and instantiate the template with at least two different sets of template arguments. The compiler might know something that you are not aware of.

Indeed, if I had done this before posting I would have found the rationale I was looking for.
Topic archived. No new replies allowed.