replay
replay

Reputation: 3799

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?

Upvotes: 4

Views: 1601

Answers (4)

Vlad from Moscow
Vlad from Moscow

Reputation: 310990

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;

Upvotes: 4

Johan
Johan

Reputation: 3778

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.

Upvotes: 5

Luca Davanzo
Luca Davanzo

Reputation: 21520

What you want is to use lambda-expression (or anonymous function).

With C++11 is now possible, but not with previous version. Problem is treated also here.

In your case i would use a method in a new class Players:

class Players {
public:
    void addPlayer(Player*);
    void removePlayer(Player*);
    vector<string> getNames() {
      std::vector<string> names;
      for(unsigned int i = 0; i != players_.size(); ++i) {
        names.push_back(players_[i]->getName());
      }
    return names;
    }
private:
vector<Player*> players_;

};

Upvotes: 0

Ivaylo Strandjev
Ivaylo Strandjev

Reputation: 70929

If you have an argument that is const ref you have no option but to construct a variable. Your const ref should point to some existing object. You can change your function to take a pointer to function and a const ref to std::vector<Player*>. Make the function take a pointer to Player and return his name.

Upvotes: 0

Related Questions