问题
Sorry for a little bit beginner question. There are vector and vector of pairs
typedef std::vector <int> TItems;
typedef std::vector < std::pair <int, int> > TPairs;
Is there any way to transform all first items in pair to another vector in one step
int main ()
{
TItems items;
TPairs pairs;
pairs.push_back (std::make_pair(1,3));
pairs.push_back (std::make_pair(5,7));
std::transform( items.begin(), items.end(), items.begin(), comp ( &pairs ) );
return 0;
}
How to design a functor?
class comp
{
private:
TPairs *pairs;
public:
comp ( TPairs *pairs_ ) : pairs ( pairs_) { }
unsigned int operator () ( const unsigned int index ) const
{
return (*pairs)[index].second != pairs->end(); //Bad idea
}
};
Maybe there is some more user friendly method without lambda expressions and loops. Thanks for your help.
回答1:
I really want you to use std::get
as the functor, because it's already provided as a library function!!
Wouldn't it be great if we could write this line!?
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
... But it's a bit more terrible than that. You need to disambiguate which get
to use:
int main() {
std::vector<int> items;
std::vector<std::pair<int, int>> pairs;
pairs.push_back(std::make_pair(1, 3));
pairs.push_back(std::make_pair(5, 7));
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items),
(const int& (*)(const std::pair<int, int>&))std::get<0>);
return 0;
}
The problem is, std::get
is overloaded to take 1. pair&
, 2. const pair&
, and 3. pair&&
as the parameters, so that it will work for any sort of pair as input. Unfortunately, the overloads get in the way of the template type deduction for std::transform
, so our original line
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
yields
error: no matching function for call to ‘transform(std::vector<std::pair<int, int> >::iterator, std::vector<std::pair<int, int> >::iterator, std::back_insert_iterator<std::vector<int> >, <unresolved overloaded function type>)’
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
^
...
/usr/include/c++/4.8/bits/stl_algo.h:4915:5: note: template argument deduction/substitution failed:
note: couldn't deduce template parameter ‘_UnaryOperation’
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
It doesn't know which overload of std::get
you are asking for when deducing the template for std::transform
, so you have to specify it manually. Casting the function pointer to the right type tells the compiler, "Hey, please use the overload where get
takes a const&
and returns a const&
!"
But at least we're using standard library components (yay)?
And in terms of number of lines, it's no worse than the other options: http://ideone.com/6dfzxz
回答2:
First of all, you should use a back_inserter as the third argument to transform so that the transformed values are pushed to the back of the vector.
Second, you need some sort of functor which takes a pair of ints and returns the first one. This should do:
int firstElement( const std::pair<int, int> &p ) {
return p.first;
}
Now, to put the pieces together:
TPairs pairs;
pairs.push_back( std::make_pair( 1, 3 ) );
pairs.push_back( std::make_pair( 5, 7 ) );
TItems items;
std::transform( pairs.begin(), pairs.end(), std::back_inserter( items ),
firstElement );
After this code, items
contains 1 and 5.
回答3:
see frerich's or kotlinski's answer for C++03.
C++11 solution with lambda:
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
[](const std::pair<int, int>& p) { return p.first; });
回答4:
How about this?
items.reserve(pairs.size());
for (size_t it = 0; it < pairs.size(); ++it) {
items.push_back(pairs[it].first);
}
Simple to understand and debug.
回答5:
How about using std::bind
?
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
std::bind(&TPairs::value_type::first, std::placeholders::_1));
(Replace std::bind
by boost::bind
for non-C++11 code)
回答6:
another possibility from C++11 would be std::mem_fn, which is similar to solution with std::bind
:
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
std::mem_fn(&std::pair<int,int>::first)
);
来源:https://stackoverflow.com/questions/9094132/c-stdtransform-vector-of-pairs-first-to-new-vector