tags:

views:

248

answers:

3
#include <vector>
#include <functional>
#include <algorithm>
using namespace std;

struct Foo
{
    int i;
    double d;
    Foo(int i, double d) :
        i(i),
        d(d)
    {}
    int getI() const { return i; }
};

int main()
{
    vector<Foo> v;
    v.push_back(Foo(1, 2.0));
    v.push_back(Foo(5, 3.0));

    vector<int> is;

    transform(v.begin(), v.end(), back_inserter(is), mem_fun_ref(&Foo::getI));

    return 0;
}

Is there a cleaner way to access a member variable then then using a member function like I have above? I know how to do it using tr1::bind, but I need to have C++03 compliant code without boost.

+8  A: 

It's absolutely unclean to need an accessor function in order to do that. But that's the current C++.

You could try using boost::bind, which does the trick quite easily, or iterate the vector explicitly, using a for( vector<int>::const_iterator it = v.begin(); .....) loop. I find the latter often resulting in clearer code when the creation of the functor becomes too much a hassle.

Or, shunning boost, create your own member-accessor shim function.

template< typename T, typename m > struct accessor_t {
   typedef m (T::*memberptr);

   memberptr acc_;

   accessor_t( memberptr acc ): acc_(acc){}
   // accessor_t( m (T::*acc) ): acc_(acc){}
   // m (T::*acc_);

   const m& operator()( const T& t ) const { return (t.*acc_); }
   m&       operator()( T& t       ) const { return (t.*acc_); }
};

template< typename T, typename m > accessor_t<T,m> accessor( m T::*acc ) {
   return accessor_t<T,m>(acc);
}

...

transform( v.begin(), v.end(), back_inserter(is), accessor( &C::i ) );
xtofl
`<trying to wrap my head around templatized member pointers>` Wouldn't it be better if that `operator()` returned a reference? `<thinks again>` Wait, `m` is a _pointer_ anyway, isn't it? `<ponders some more>` Boy, I have never exactly fallen in love with member pointers, and although I fiddled with them (even with templatized ones) myself once in a while, I do have a hard time understanding this. `<sheepish look>`
sbi
It could also return a const reference (since T is passed by const reference to function operator). In case of an int, it doesn't matter, though.
UncleBens
j_random_hacker
@sbi: tried to make it more readable by introducing a typedef. @j_random_hacker: I was a bit too enthousiastic doing so... rolled that back.
xtofl
@UncleBens and @sbi: indeed, the `operator()` should return a reference to the member itself; const if a const argument was provided, non-const otherwise. Corrected that.
xtofl
@xtofl: It's not your code that's crashing my internal parser. That `m (T::*)` just looks so weird...
sbi
BTW, that `accessor()` function template probably should be marked `inline`, even though most modern compilers will inline that function no matter whether you do this.
sbi
@xtofl: Much better, +1. :)
j_random_hacker
+2  A: 

The most clear way for me is boost::bind:

#include <boost/bind.hpp>

...

transform(v.begin(), v.end(), back_inserter(is), bind( &Foo::i, _1 ) );

Surely you could create your own member access function, but I believe it will make your code less readable. boost::bind is widely known library, so using it will make your code pretty readable and no need to read your helper functions (which could contain bugs occasionally)

The second way I prefer is just to use for-loop (in this particular case):

for ( vector<Foo>::const_iterator it = v.begin(), it != v.end(); ++it )
    is.push_back( it->i );

May be it is not fashionable to use such simple loops, but they are very clear.

Kirill V. Lyadvinsky
I agree, but unfortunately I can't use boost or tr1 bind.
I'm under the impression that boost/tr1 mem_fn can also do it (without the placeholder), but it seems xtofl has the answer in your situation.
UncleBens
+3  A: 

Like std::pair you could write access-or objects.

#include <vector>
#include <algorithm>

struct Foo
{
    int i;
    double d;
};

struct GetI { int    operator()(Foo const& o) const { return o.i;}};
struct GetD { double operator()(Foo const& o) const { return o.d;}};

int main()
{
    std::vector<Foo>    v;
    std::vector<int>    t;
    std::transform(v.begin(), v.end(), std::back_inserter(t),GetI() );
}

Note: You should look at std::pair<T1,T2>
And its accessors: std::select1st<T1> and std::select2nd<T2>

Martin York