8

Is there any shortcut to convert from std::vector<T> to std::vector<T*> or std::vector<T&>?

Essentially I want to replace:

std::vector<T> source; std::vector<T*> target; for(auto it = source.begin(); it != source.end(); it++) { target.push_back(&(*it)); } 

with a single line.

To provide some context: I have one set of functions which do their computation on a std::vector<Polygon> and some which require std::vector<Polygon*>. So I need to convert back and forth a couple of times, because the interface of these functions is not supposed to change.

3
  • 2
    Keep in mind you should do target.reserve(source.size()) to avoid unnecessary memory allocations. Commented Jul 29, 2011 at 7:53
  • Is this safe? target will contain pointers to the objects stored in source. If source changes (i.e. an element is added), then the whole of source could be moved. It will be alright if you make sure that you regenerate target every time source changes. Commented Jul 29, 2011 at 11:39
  • 2
    @DanS: I got these functions which require std::vector<T*>, since nobody changes source while I call these, everything should be fine. Commented Jul 29, 2011 at 14:19

3 Answers 3

12

Although what you describe sounds strange (without context), using std::transform you could do the actual copying/transformation in one line of code :

transform(source.begin(), source.end(), target.begin(), getPointer); 

given that target has sufficient room for all data (can be achieved with std::vector::resize eg.), and given this unary operator :

template<typename T> T* getPointer(T& t) { return &t; } 
Sign up to request clarification or add additional context in comments.

3 Comments

If target is empty to begin with then using back_inserter(target) in place of target.begin() avoids the need to resize. But a reserve is still slightly worthwhile to avoid the vector re-allocating itself more than necessary.
But the 1 million dollar question is, where do those pointers point to after you dispose of the first vector (which will destruct all objects inside it)? -- still +1 for a very creative solution.
@Damon: It is the responsibility of the person who creates the target array to make sure the pointers remain valid.
7

If you have got c++0x the following code does the trick (c++0x needed for lambdas):

template <typename T> std::vector<T*> convertFrom(std::vector<T>& source) { std::vector<T*> target(source.size()); std::transform(source.begin(), source.end(), target.begin(), [](T& t) { return &t; }); return target; } 

One side note: Make sure the source vector is not cleaned up before the last usage of the target vector as it is the owner of the actual objects. The target vector only contains the pointers.

Comments

1

Another idea to my previous answer, is to use boost iterators.

Since it's sufficiently different, I thought I'd post it as a new response :

First using the boost::transform_iterator :

template<typename T> T* getPointer(T& t) { return &t; } boost::transform_iterator<T* (*)(T&), std::vector<T>::iterator> begin(source.begin(), getPointer), end(source.end(), getPointer); std::vector<T*> target(begin, end); 

Or to construct the source vector from the target vector (if that direction also works for you), you can use the boost::indirect_iterator :

boost::indirect_iterator<std::vector<T*>::iterator> begin(target.begin()), end(target.end()); std::vector<T> source(begin, end); 

It might not be as pretty, but it's effective.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.