How do I display the contents of a std::vector of my own datatype and display the name on the console using std::copy?

Viewed 84

I would like to display the contents of the std::vector<User> using std::copy in a similar way to how I've achieved it through std::for_each in the code below.

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>
class User {
private:
    std::string m_sName;
public:
    User(std::string sName):m_sName(sName) {}
    std::string GetName()const {
        return m_sName;
    }
};
int main()
{
    std::vector<User> vectNames;
    vectNames.emplace_back("Jack");
    vectNames.emplace_back("George");
    vectNames.emplace_back("Jose");
//How can I get std::copy to do what the std::for_each is doing?
    std::copy(vectNames.begin(), vectNames.end(), std::ostream_iterator<User>(std::cout, "\n"));
//The following line is what I really would like std::copy to do. 
    std::for_each(vectNames.begin(), vectNames.end(), [](const User &user) {std::cout << user.GetName() << "\n"; });
    
}
2 Answers

You can simply overload the ostream operator:

#include <iostream>
#include <vector>
#include <string>
#include <algorithm>

class User {
private:
    std::string m_sName;
public:
    User(std::string sName):m_sName(sName) {}
    std::string GetName()const {
        return m_sName;
    }
    void print(std::ostream& where) const
        { where << m_sName; }
};

/// Overloaded ostream operator
std::ostream& operator<< (std::ostream& out, const User& user)
{
    user.print(out);
    return out;
}

int main()
{
    std::vector<User> vectNames;
    vectNames.emplace_back("Jack");
    vectNames.emplace_back("George");
    vectNames.emplace_back("Jose");
    
    std::copy(vectNames.begin(), vectNames.end(), std::ostream_iterator<User>(std::cout, "\n"));
}

In general I always have an overloaded ostream as part of classes that store and output information.

Because of your comment I am a new programmer and am experimenting with different ideas, I'm posting this answer as an alternative approach which also shows some algorithm that you might enthusiastic about.

I think that std::copy isn't the right tool if what you want to do is both

  1. calling a member function on the objects in a collection, and only after that
  2. copying/printing them to screen

One, and maybe the best, approach is the one in the atru's answer, which basically solves only point 2 with std::copy (and this is something you already knew how to do it, based on your question), and point 1 by overloading <<, which makes the trick.

The alternative I propose is based on the idea that "calling a member function on every object of a collection" is actually a transform operation of that collection. std::transform, however, just like std::copy acts on iterators, not on ranges, so they cannot be easily composed with one another.

Here comes Boost, with boost::copy and boost::adaptors::transformed which allow you to do this:

boost::copy(
        vectNames | transformed([](auto const& x){ return x.GetName(); }),
        std::ostream_iterator<std::string>(std::cout, "\n")
    );

where vectNames is "piped into" transformed, which applies the lambda on every element of the collection, resulting in a new temporary collection, which is the argument to boost::copy. If you were to use std::copy, you would have to store the temporary somewhere, e.g. in temp, before passing to std::copy its iterators temp.begin() and temp.end().

The full example is below. I hope it will give you some insight in different, more functional, approaches.

#include <functional>
#include <iostream>
#include <vector>
#include <string>
#include <boost/range/adaptor/transformed.hpp>
#include <boost/range/algorithm/copy.hpp>
class User {
private:
    std::string m_sName;
public:
    User(std::string sName):m_sName(sName) {}
    std::string GetName()const {
        return m_sName;
    }
};
int main()
{
    std::vector<User> vectNames;
    vectNames.emplace_back("Jack");
    vectNames.emplace_back("George");
    vectNames.emplace_back("Jose");

    using boost::adaptors::transformed;
    boost::copy(
        vectNames | transformed([](auto const& x){ return x.GetName(); }),
        std::ostream_iterator<std::string>(std::cout, "\n")
    );
}

Related