2012-02-01 11 views
15

Lo siento por una pequeña pregunta para principiantes. Hay vector y el vector de paresC++ std :: transform vector of pairs-> first to new vector

typedef std::vector <int> TItems; 
typedef std::vector < std::pair <int, int> > TPairs; 

¿Hay alguna manera de transformar todos los primeros elementos de par a otro vector en un solo paso

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

Cómo diseñar un funtor?

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

Tal vez haya algún método más fácil de usar sin expresiones lambda y bucles. Gracias por tu ayuda.

Respuesta

3

Realmente quiero que uses std::get como el funtor, porque ya está previsto como una función de la biblioteca !!

¿No sería genial si pudiéramos escribir esta línea?

std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 

... Pero es un poco más terrible que eso. Es necesario para eliminar la ambigüedad, que get de usar:

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

El problema es, std::getis overloaded a tomar 1. pair&, 2. const pair&, y 3. pair&& como los parámetros, por lo que funcionará para cualquier tipo de pareja como entrada.Por desgracia, las sobrecargas en el camino de la deducción tipo de plantilla para std::transform, por lo que nuestra línea original

std::transform(pairs.begin(), pairs.end(), std::back_inserter(items), std::get<0>); 

rendimientos

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

No sé a qué sobrecarga de std::get está solicitando al deducir la plantilla para std::transform, por lo que debe especificarla manualmente. Al colocar el puntero de función en el tipo correcto, se le dice al compilador: "¡Oye, usa la sobrecarga, donde get toma const& y devuelve const&!"

Pero al menos estamos usando componentes de biblioteca estándar (yay)?

Y en cuanto a número de líneas, no es peor que las otras opciones: http://ideone.com/6dfzxz

+1

¿Alguien puede pensar en alguna mejora? Sería genial poder usar 'std :: get' de esta manera. ... Realmente debería estar usando 'reinterperet_cast &)> (std :: get <0>)', pero eso parece incluso peor ... – NHDaly

+0

Creo que es posible reemplazar el elenco "duro" con la función get envuelta dentro de una lambda para la cual se pueden especificar los argumentos –

3

¿Qué tal esto?

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

Fácil de entender y depurar.

+0

@ Kotlinski: Gracias, pero esta es una solución común. Me gustaría encontrar una solución de un paso sin bucle, si es posible. – justik

+1

Usted pidió algo fácil de usar, entonces sería erróneo publicar una respuesta con alguna atrocidad de C++ :) –

+0

+1: más simple en este caso. ¿Por qué evitar bucles si se simplifican? – stefaanv

15

En primer lugar, debe utilizar un back_inserter como tercer argumento para transform de modo que los valores transformados se coloquen en la parte posterior del vector.

En segundo lugar, necesita algún tipo de functor que toma un par de entradas y devuelve la primera. Esto debería hacer:

int firstElement(const std::pair<int, int> &p) { 
    return p.first; 
} 

Ahora, para poner las piezas juntas:

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

Después de este código, items contiene 1 y 5.

+0

@ Freirich Raabe: Gracias, funciona. – justik

+2

¿Existe alguna manera inteligente de usar [std :: get <0>] (http://en.cppreference.com/w/cpp/utility/tuple/get) en lugar de su función personalizada? – NHDaly

2

Cómo sobre el uso std::bind?

std::transform(pairs.begin(), 
       pairs.end(), 
       std::back_inserter(items), 
       std::bind(&TPairs::value_type::first, std::placeholders::_1)); 

(Reemplazar std::bind por boost::bind para el código de no-C++ 11)

10

ver de Frerich o la respuesta de Kotlinski para C++ 03.

C++ 11 solución con lambda:

std::transform(pairs.begin(), 
       pairs.end(), 
       std::back_inserter(items), 
       [](const std::pair<int, int>& p) { return p.first; }); 
+0

Vaya, no noté el requisito de "no lambda", pero ¿por qué cuando es sencillo y forma parte del idioma? – stefaanv

+0

Creo que no es parte del lenguaje C++ que la mayoría de la gente de aquí puede usar (debido a limitaciones del compilador o debido a algunos requisitos del lugar de trabajo). –