Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ best way to build vector of strings from multiple returns of function calls

I'm practicing C++, so this is not code that will go into production, but I'm very curious to know:

I have a vector of pointers to objects of type Player:

std::vector<Player*> _players;

Each player returns me his name as std::string when I call it's method get_name(), for example:

std::string player0_name = _players[0]->get_name();

I want to pass all player names to a function that expects them as reference to a vector of strings:

void all_player_names( std::vector< std::string >& );

Now I know it would be easy to do this via some temporary variable. I could first create a vector of strings, store all player names there, then pass it to the function all_player_names as reference.

But I'm looking for a way of doing that without having to create a temporary variable. It should be something similar like a list comprehension in Python. It has to iterate over the array of Player pointers, call the get_name() function on each of them, build a vector out of the returned strings and directly pass it to the function all_player_names. I'm assuming it should be possible with lambda functions and some algorithm from the STL, but I don't know which one.

So it should look more or less like that:

all_player_names(<function that i'm looking for>(
    _players, [](Player* p) { return p->get_name();}
);

Is there such an algorithm in the STL?

like image 390
replay Avatar asked Jan 12 '23 22:01

replay


2 Answers

Easiest way would be using accumulate and using a temporary variable that will be passed through the binary op and returned:

std::accumulate(_players.begin(), _players.end(), 
 std::vector<std::string>(),
 [](std::vector<std::string>& vector, const Player* elem) 
   {
    vector.push_back(elem->get_name());
    return vector;
   });

Thanks to move semantics it should have almost no performance overhead in C++11.

like image 193
Johan Avatar answered Jan 16 '23 19:01

Johan


In my opinion it is better to use standard algorithm std::transform instead of std::accumulate. The semantic of std::transform is more clear in this case compared with the semantic of std::accumulate. For example

    struct Player
    {
    public:
        Player( const std::string &s ) : s( s ) {}
        const std::string & get_name() const
        {
            return s;
        }
    private:
        std::string s;
    };

    std::vector<Player *> v1;

    v1.push_back( new Player( "Hello" ) );
    v1.push_back( new Player( "Player" ) );

    std::vector<std::string> v2;
    v2.reserve( v1.size() );


    std::transform( v1.begin(), v1.end(), std::back_inserter( v2 ), 
                    std::mem_fun( &Player::get_name ) );

    for ( const std::string &s : v2 ) std::cout << s << ' ';
    std::cout << std::endl;
like image 23
Vlad from Moscow Avatar answered Jan 16 '23 17:01

Vlad from Moscow