PaulH
PaulH

Reputation: 7843

selecting a single member from each structure in a std::vector into another vector

I have a Visual Studio 2008 C++ project where I would like to copy one struct element of a vector of that struct type to a new vector. For example:

struct Foo {
    int a;
    long b;
};

std::vector< Foo > v1;
std::vector< long > v2;

for( std::vector< Foo >::const_iterator it = v1.begin(); it != v1.end(); ++it )
{
    v2.push_back( it->b );
}

Is there a better/more elegant way than this?

Thanks, PaulH

Upvotes: 15

Views: 4323

Answers (2)

6502
6502

Reputation: 114461

It really depends on what you mean with "better".

If you mean if the same can be written by using template trickery then the answer is yes:

template<typename C, typename T>
struct MemberGetter
{
    T C::*mp;
    MemberGetter(T C::*mp) : mp(mp) { }
    T operator()(const C& c) { return c.*mp; }
};

struct Foo
{
    double dm;
};

...
std::vector<double> x;
std::vector<Foo> y;

std::transform(y.begin(), y.end(),
               std::back_inserter(x),
               MemberGetter<Foo, double>(&Foo::dm));

This is in my opinion worse than an explicit loop, but has the advantage that the member pointer (i.e. which part of the structure you are copying) can be a runtime parameter.

If the member you need to copy is instead known and fixed then I'd say that the explicit loop is the best way (I can barely imagine corner cases in which using a similar template where the member pointer is a template parameter makes any sense).

Upvotes: 1

James McNellis
James McNellis

Reputation: 355039

In Visual C++ 2008, no, this is about as "elegant" as it gets. The Standard Library provides algorithms that can be used to manipulate containers, but in most scenarios--especially in simple use cases like this one--they are far too cumbersome to use.

C++11 adds lambda expressions to C++. Visual C++ 2010 and recent versions of other C++ compilers support this C++11 feature. With lambda expressions, you can easily use the transform algorithm for your task:

std::transform(v1.begin(), v1.end(), std::back_inserter(v2),
               [](Foo const& x) { return x.b; });

Without lambda expressions, you'd have to define a function to extract the b element from the struct:

long get_b(Foo const& x) { return x.b; }

You could then use this function with the transform algorithm:

std::transform(v1.begin(), v1.end(), std::back_inserter(v2), get_b);

However, for simple use cases like this, this can quickly lead to unwieldy code as it is difficult to neatly keep all of the related functions together.

Upvotes: 19

Related Questions