C++ std::transform вектор пар-> сначала в новый вектор
Извините за немного начинающий вопрос. Есть вектор и вектор пар
typedef std::vector <int> TItems;
typedef std::vector < std::pair <int, int> > TPairs;
Есть ли способ преобразовать все первые элементы в паре в другой вектор за один шаг?
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;
}
Как спроектировать функтор?
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
}
};
Может быть, есть более удобный для пользователя метод без лямбда-выражений и циклов. Спасибо за вашу помощь.
5 ответов
Я действительно хочу, чтобы вы использовали std::get
как функтор, потому что он уже предоставлен как функция библиотеки!!
Не было бы замечательно, если бы мы могли написать эту строку!?
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
... Но это немного страшнее, чем это. Вы должны понять, какие get
использовать:
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;
}
Проблема в, std::get
перегружен, чтобы принять 1. pair&
2. const pair&
и 3. pair&&
в качестве параметров, так что он будет работать для любой пары в качестве входных данных. К сожалению, перегрузки мешают выводу типа шаблона для std::transform
так наша оригинальная линия
std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>);
доходность
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>);
Он не знает, какая перегрузка std::get
вы просите при выводе шаблона для std::transform
, поэтому вы должны указать это вручную. Приведение указателя функции к нужному типу говорит компилятору: "Эй, пожалуйста, используйте перегрузку, где get
занимает const&
и возвращает const&
!"
Но, по крайней мере, мы используем стандартные библиотечные компоненты (ура)?
И с точки зрения количества строк, это не хуже, чем другие варианты: http://ideone.com/6dfzxz
Прежде всего, вы должны использовать back_inserter
в качестве третьего аргумента transform
так что преобразованные значения помещаются в конец вектора.
Во-вторых, вам нужен какой-то функтор, который берет пару целых и возвращает первый. Это должно сделать:
int firstElement( const std::pair<int, int> &p ) {
return p.first;
}
Теперь, чтобы собрать кусочки вместе:
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 );
После этого кода items
содержит 1 и 5.
См. ответ Фририха или Котлинского для C++ 03.
C++ 11 решение с лямбда-выражением:
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
[](const std::pair<int, int>& p) { return p.first; });
Как насчет этого?
items.reserve(pairs.size());
for (size_t it = 0; it < pairs.size(); ++it) {
items.push_back(pairs[it].first);
}
Простой для понимания и отладки.
Другая возможность из C++11 будет std::mem_fn
, который похож на решение с std::bind
:
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
std::mem_fn(&std::pair<int,int>::first)
);
Как насчет использования std::bind
?
std::transform(pairs.begin(),
pairs.end(),
std::back_inserter(items),
std::bind(&TPairs::value_type::first, std::placeholders::_1));
(Заменить std::bind
от boost::bind
для не-C++11 кода)