2012-07-27 11 views
7

Dado un std::vector que contiene objetos de MyClass. ¿Cómo puedo crear otro vector que contenga solo datos de un miembro de MyClass usando std::copy? Supongo que tendría que implementar un back_inserter personalizado, pero no pude encontrar la manera de hacerlo hasta el momento.Insertador personalizado para std :: copy

struct MyClass { 
    int a; 
} 

std::vector<MyClass> vec1; 

// I could copy that to another vector of type MyClass using std::copy. 
std::copy(vec1.begin(), vec1.end(); std::back_inserter(someOtherVec) 

// However I want just the data of the member a, how can I do that using std::copy? 
std::vector<int> vec2; 
+1

'std :: copy' es para copiado simple, sin modificar los elementos. 'std :: transform' le permite aplicar una transformación a cada elemento y luego almacenar la salida de la transformación. Que es exactamente lo que quieres :) – jalf

+0

¡gran cantidad de respuestas, gracias! – Nils

Respuesta

15

Use std::transform para eso.

std::transform(vec1.begin(), vec1.end(), std::back_inserter(vec2), 
       [](const MyClass& cls) { return cls.a; }); 

(Si no puede utilizar C++ 11, se puede hacer una función de objeto a sí mismo:

struct AGetter { int operator()(const MyClass& cls) const { return cls.a; } }; 

std::transform(vec1.begin(), vec1.end(), std::back_inserter(vec2), AGetter()); 

o utilizar std::tr1::bind si se puede usar TR1:

std::transform(vec1.begin(), vec1.end(), std::back_inserter(vec2), 
       std::tr1::bind(&MyClass::a, std::tr1::placeholders::_1)); 

BTW, como @Nawaz comentado a continuación, haga un .reserve() para evitar la reasignación innecesaria durante la copia.

vec2.reserve(vec1.size()); 
std::transform(...); 
+0

Ojalá tuviera C++ 11 en el trabajo ... pero también se puede hacer sin lambdas. – Nils

+0

Es mejor llamar 'reserve()' en 'vec2' antes de usar' std :: back_inserter'. – Nawaz

+1

Puede usar 'std :: bind' con' std :: transform' para omitir todo el 'Agetter'. – Flexo

4

que desea utilizar std::transform no std::copy y std::bind para unirse a un puntero a una variable miembro:

#include <algorithm> 
#include <iterator> 
#include <vector> 
#include <iostream> 
#include <functional> 

struct foo { 
    int a; 
}; 

int main() { 
    const std::vector<foo> f = {{0},{1},{2}}; 
    std::vector<int> out; 

    out.reserve(f.size()); 
    std::transform(f.begin(), f.end(), std::back_inserter(out), 
       std::bind(&foo::a, std::placeholders::_1)); 

    // Print to prove it worked: 
    std::copy(out.begin(), out.end(), std::ostream_iterator<int>(std::cout, "\n")); 
} 

Mi ejemplo es C++ 11, pero si se salta el vector práctico y initalization use boost::bind, en su lugar, esto funciona igual de bien sin C++ 11.

+1

Si está usando C++ 11, ¿por qué no usar lambda en lugar de 'std :: bind'? – Nawaz

+0

Para cosas simples prefiero la sintaxis de la carpeta personalmente. Es una pena que los marcadores de posición tengan su propio espacio de nombres, pero incluso con eso es menos detallado que el equivalente lambda. – Flexo

Cuestiones relacionadas