4

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?

2
  • build a vector out of the returned strings ... that is a temporary variable, and not a comprehension. A list comprehension would be something more like an iterator pair, where dereferencing the iterator generates values on the fly Commented Nov 14, 2013 at 8:37
  • To be honest, looking at the answers below, I would prefer the for-loop. I mean, it still doesn't come close to var names = players.Select(p => p.Name) :-) Commented Nov 14, 2013 at 9:26

4 Answers 4

5

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.

Sign up to request clarification or add additional context in comments.

2 Comments

you should create the vector with the empty constructor if you plan to call push_back like you do.
+1 your solution is good. Still please indicate in the answer that you still create a temporary variable and pass it as argument. There is no way to avoid this with this function declaration.
4

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;

Comments

0

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.

Comments

0

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_;

};

1 Comment

I somehow fail to see the part of the code that did not work in pre - c++11. Also I don't quite see how this solves OP's problem.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.