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"; });
    
}
4
  • 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 it, and the result of the transformation is what you want to print/"copy" to the screen.
    – Enlico
    Commented Oct 31, 2020 at 14:48
  • 2
    I am a new programmer and am experimenting with different ideas. Commented Oct 31, 2020 at 15:39
  • 1
    Good to know. I've added an answer that you might like :)
    – Enlico
    Commented Oct 31, 2020 at 16:39
  • Wow! Thanks so much! I really appreciate you showing me a different approach! Commented Oct 31, 2020 at 21:29

3 Answers 3

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.

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

5
  • 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! Commented Oct 31, 2020 at 21:24
  • My IDE (Visual Studio 2019) isn't recognising the following includes: #include <boost/range/adaptor/transformed.hpp> #include <boost/range/algorithm/copy.hpp> Any potential solution to this? I have it in C++ 17. Commented Oct 31, 2020 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
    Commented Oct 31, 2020 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
    Commented Nov 1, 2020 at 5: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
    Commented Nov 1, 2020 at 6: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")
  );
}

Not the answer you're looking for? Browse other questions tagged or ask your own question.