4
#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.

3 Answers 3

8

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 ) ); 
Sign up to request clarification or add additional context in comments.

7 Comments

<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>
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.
Your declaration for the parameter to accessor() is unable to deduce T and m -- either specify the full type here (i.e. m T::*acc), which makes these template parameters deducible, or specify all template arguments at the call site (i.e. accessor<C, int>(&C::i)).
@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.
@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.
|
3

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>

Comments

2

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.

2 Comments

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.