2

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"; });
    
}
Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    Can you tell us a bit more why you need `std::copy`? `std::for_each` seems indeed a better tool to do what you do. You are not really copying `vectNames` anywhere; in fact, you are [transforming](https://en.cppreference.com/w/cpp/algorithm/transform) it, and the result of the transformation is what you want to print/"copy" to the screen. – Enlico Oct 31 '20 at 14:48
  • 2
    I am a new programmer and am experimenting with different ideas. – George Austin Bradley Oct 31 '20 at 15:39
  • 1
    Good to know. I've added an answer that you might like :) – Enlico Oct 31 '20 at 16:39
  • Wow! Thanks so much! I really appreciate you showing me a different approach! – George Austin Bradley Oct 31 '20 at 21:29

3 Answers3

3

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.

atru
  • 4,699
  • 2
  • 18
  • 19
2

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")
    );
}

Enlico
  • 23,259
  • 6
  • 48
  • 102
  • 1
    Thanks so much! I’ve marked this one as best answer! I find it really fascinating some of the other ways you’ve shown me! This has added to my knowledge! – George Austin Bradley Oct 31 '20 at 21:24
  • My IDE (Visual Studio 2019) isn't recognising the following includes: #include #include Any potential solution to this? I have it in C++ 17. – George Austin Bradley Oct 31 '20 at 23:03
  • 1
    @GeorgeAustinBradley maybe installing Boost? I'm on Arch Linux and everything works out of the box with it. I guess you're on Windows, and it's always a pain with it. Probably there's already done existing question about how to install Boost libraries. – Enlico Oct 31 '20 at 23:30
  • 2
    That isn't about Windows, boost always need be installed and it apparently was in your ArchLinux . @GeorgeAustinBradley, best find info for boost in relation to the particular MS's product to avoid surprises. – zdim Nov 01 '20 at 05:03
  • 1
    @zdim, yes, I probably mislead the reader with the _out of the box_ part; what I mean is that installing Boost on Arch did not take more than one command, namely `pacman -S boost`. – Enlico Nov 01 '20 at 06:23
0

With c++20 you do not need anymore an external library. You have ranges and views in the language.

The code becomes:

#include <functional>
#include <iostream>
#include <vector>
#include <string>
#include <ranges>
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");

  std::ranges::copy(
      vectNames | std::views::transform([](auto const& x) { return x.GetName(); }),
      std::ostream_iterator<std::string>(std::cout, "\n")
  );
}
Dundo
  • 714
  • 8
  • 12