Search code examples
c++algorithmstliteratorostream

C++ Output via copy()


I'm playing with C++ and the STL and I've attempted to copy a deque into a list and print the list via copy() and ostream_iterator. For some reason, the contents of the list I copied to doesn't print unless I access the elements via front(), back(), or at(). Why do the first two printing attempts fail:

#include <iostream>
#include <fstream>
#include  <deque>
#include <algorithm>
#include <iterator>
#include <list>
using namespace std;

void alterParticle(string&);

int main(){

   string tmp_str;
   deque<string> d;
   list<string> l;

   ifstream in("foo.txt");
   if(!in.is_open()){
      cout << "Error opening file" << endl;
      return 1;
   }
   while(in){
      getline(in,tmp_str);
      d.push_back(tmp_str);
   }

   for_each(d.begin(),d.end(),alterParticle);

   copy(d.begin(),d.end(),ostream_iterator<string>(cout,"\n"));

   ostream_iterator<string> out(cout,"\n");
   copy_if(d.begin(),d.end(),out,
         [](const string& s){
            if(s.find("fooparticle")!= string::npos)
               return true;
            return false;
         });

   copy_if(d.begin(),d.end(),l.begin(),
      [](const string& s){
         if(s.find("fooparticle")!= string::npos)
            return true;
         return false;
      });

   cout << "First try: " << endl;
   for(string s : l)
      cout << s << endl;

   cout << "Second try: " << endl;
   copy(l.begin(),l.end(),out);

   cout << "Last try: " << l.front() << endl;

   return 0;
}

void alterParticle(string& s){
   int fpos = s.find("quark");
   string rep_str{"quark"};
   if(fpos != string::npos){
      s.replace(s.find(rep_str),rep_str.length(),"fooparticle");
   }
}

Output:

fooparticle 10 11.4
neutrino 7 20.5
electron 5 6.7
proton 8 9.5

fooparticle 10 11.4
First try:
Second try:
Last try: fooparticle 10 11.4

Edit:

Just so it's easier to see why this didn't work for anyone who asks the same question, here are the semantics of copy_if(). It makes it pretty clear that it does not expand the container:

template <class InputIterator, class OutputIterator, class UnaryPredicate>
  OutputIterator copy_if (InputIterator first, InputIterator last,
                          OutputIterator result, UnaryPredicate pred)
{
  while (first!=last) {
    if (pred(*first)) {
      *result = *first;
      ++result;
    }
    ++first;
  }
  return result;
}

Solution

  • copy and copy_if do not add new elements to the list, they assume there are existing elements to copy into. Your list is initially empty and thus you are writing to the begin() == end() iterator of the list. This does not increase the list size (which is why the first two attempts print nothing), but if you access the (not actually existing) first list member, you might get the result that was written there.

    Needless to say, assigning to the end() iterator is undefined behavior.

    You can keep using copy and friends if you use an insert_iterator (you'd generally use back_inserter), similar to the ostream_iterator you are already using.