Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Custom inserter for std::copy

Tags:

c++

std

Given a std::vector which holds objects of MyClass. How can I create another vector which holds just data of one member of MyClass using std::copy? I guess I would have to implement a custom back_inserter but I could not figure out how to do this so far.

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;
like image 354
Nils Avatar asked Jul 27 '12 11:07

Nils


People also ask

What is STD inserter?

std::inserter in C++ std::inserter constructs an insert iterator that inserts new elements into x in successive locations starting at the position pointed by it.

What is a back_ inserter?

A back-insert iterator is a special type of output iterator designed to allow algorithms that usually overwrite elements (such as copy) to instead insert new elements automatically at the end of the container. Syntax: std::back_inserter (Container& x); x: Container in which new elements will be inserted at the end.

How does STD copy work?

std::copy, std::copy_if. Copies the elements in the range, defined by [first, last) , to another range beginning at d_first . 1) Copies all elements in the range [first, last) starting from first and proceeding to last - 1. The behavior is undefined if d_first is within the range [first, last) .


2 Answers

Use std::transform for that.

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

(If you can't use C++11, you could make a function object yourself:

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

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

or use std::tr1::bind if you can use TR1:

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

BTW, as @Nawaz commented below, do a .reserve() to prevent unnecessary reallocation during the copy.

vec2.reserve(vec1.size());
std::transform(...);
like image 161
kennytm Avatar answered Oct 17 '22 00:10

kennytm


You want to use std::transform not std::copy and std::bind to bind to a pointer to a member variable:

#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"));
}

My example is C++11, but if you skip the handy vector initalization and use boost::bind instead this works just as well without C++11.

like image 28
Flexo Avatar answered Oct 17 '22 02:10

Flexo