C ++ std :: transform вектор пар-> сначала в новый вектор - PullRequest
19 голосов
/ 01 февраля 2012

Извините за немного начинающий вопрос.Есть вектор и вектор пар

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

Может быть, есть более удобный метод без лямбда-выражений и циклов.Спасибо за вашу помощь.

Ответы [ 6 ]

18 голосов
/ 01 февраля 2012

Прежде всего, вы должны использовать 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.

13 голосов
/ 01 февраля 2012

см. Ответ Фририха или Котлинского для C ++ 03.

C ++ 11 решение с лямбда-выражением:

std::transform(pairs.begin(), 
               pairs.end(), 
               std::back_inserter(items), 
               [](const std::pair<int, int>& p) { return p.first; });
7 голосов
/ 14 августа 2015

Я действительно хочу, чтобы вы использовали 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

3 голосов
/ 01 февраля 2012

Как насчет этого?

items.reserve(pairs.size());
for (size_t it = 0; it < pairs.size(); ++it) {
    items.push_back(pairs[it].first);
}

Простой для понимания и отладки.

2 голосов
/ 01 февраля 2012

Как насчет использования 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 кода)

1 голос
/ 03 декабря 2018

другая возможность из 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)               
);
...