Using mem_fun_ref on overloaded member function.

T

tmak

I am having trouble using mem_fun_ref on an overloaded member
function. Here's my code (using gcc 4.x):

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

int main()
{
vector<string> vs;
vs.push_back(string("One"));
vs.push_back(string("Two"));

for_each(vs.begin(),vs.end(), bind2nd (mem_fun_ref
( &string::push_back), 's' ) );
cout << vs[0] << " and " << vs[1] << endl; //prints Ones and Twos as
expeced

//ERROR: Following line does not compile
for_each(vs.begin(),vs.end(), bind2nd(mem_fun_ref
(&string::append),"ome"));
cout << vs[0] << " and " << vs[1] << endl ; //should output Onesome
and Twosome


//This works, but not how I want to do it
for (vector<string>::iterator it=vs.begin(); it!= vs.end(); it++)
it->append("ome");
cout << vs[0] << " and " << vs[1] << endl ; //outputs Onesome and
Twosome

}

gcc (4.0.1 on Max OS X) gives this :
mem_fun_fun.cpp: In function ‘int main()’:
2 mem_fun_fun.cpp|20| error: no matching function for call to
‘mem_fun_ref(<unknown type>)’

I am guessing the compiler has no idea which of the overloaded
string::append functions to use. Any ideas on solving this.
 
S

SG

    //ERROR: Following line  does not compile
    for_each(vs.begin(),vs.end(),
bind2nd(mem_fun_ref(&string::append),"ome") );

string::append is overloaded and the compiler doesn't know what
function you meant. So, string::append actually refers to a set of
functions. Of course, if the compiler tries to deduce function
template's template parameter (the function pointer type), this won't
work.

Try:
typedef string& (string::*pmf_type)(char const*);
.....
bind2nd(mem_fun_ref(
static_cast<pmf_type>(&string::append)
),"ome");

A better alternative might be to use your own functor.

template<typename T, typename U>
struct appendor : std::binary_function<T,U,void> {
void operator()(T& t, U const& u) const {
t.append(u);
}
};
.....
bind2nd(appendor<string,char const*>(),"ome");


Cheers!
SG
 
T

tmak

Thanks for your help SG. The functor approach worked for me.
I modified it by making a template function "for_all" instead to avoid
needing the bind2nd call, as well as having to specify the argument
type to appendor.

Here's my final version (please comment if you see a problem -I'm
trying to learn templates);

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

template<typename T> class appendor{
public:
template<class A> T& operator ()(T& t,const A& a) const { return
t.append(a);}
template<class A> T& operator()(T& t,A& a) const { return t.append
(a); }
};

template<class V,class F,class A>void for_all(V& vec,const F&
func,const A& arg){
for (typename V::iterator it=vec.begin(); it != vec.end(); it ++){
func(*it,arg);
}
}


int main()
{
vector<string> vs;
vs.push_back(string("One"));
vs.push_back(string("Two"));

for_each(vs.begin(),vs.end(),bind2nd(mem_fun_ref
(&string::push_back),'s'));
cout << vs[0] << " and " << vs[1] << endl; //prints Ones and Twos

for_all(vs,appendor<string>(),"ome");
for_all(vs,appendor<string>(),string(5,'!'));

cout << vs[0] << " and " << vs[1] << endl ; ////Onesome!!!!! and
Twosome!!!!!

}
 
S

SG

tmak said:
Thanks for your help SG. The functor approach worked for me.
I modified it by making a template function "for_all" instead to avoid
needing the bind2nd call, as well as having to specify the  argument
type to appendor.

Here's my final version (please comment if you see a problem -I'm
trying to learn templates);

#include <string>
#include <iostream>
#include <algorithm>
#include <vector>
#include <functional>

using namespace std;

template<typename T> class appendor
{
public:
  template<class A>
T& operator()(T& t, const A& a) const {return t.append(a);}
  template<class A>
T& operator()(T& t, A& a) const {return t.append(a);}
};

template<class V,class F,class A>
void for_all(V& vec,const F& func,const A& arg)
{
  for (typename V::iterator it=vec.begin(); it != vec.end(); it ++){
     func(*it,arg);
  }
}

V might be deduced to be "const something" in which case begin() and
end() will return const_iterators. const_iterators are not convertible
to their non-const counter part, so this will result in a compilation
error. Also, avoid post-increment and post-decrement if pre-increment
or pre-decrement is sufficient.

In C++0x you will be able to write

template<class Container, class Functor, class...Args>
Functor for_all(Container & cont, Functor f, const Args&...args)
{
auto beg = vec.begin();
auto const end = vec.end();
for (; beg!=end; ++beg){
func(*beg,args...);
}
return func;
}

to solve the iterator type problem. But then again, you could just as
well reuse std::for_each with a lambda expression or use the new for-
range loop.

In C++03 you can overload your for_all function template with another
version that takes a ref-to-const for the container and use typename
Container::const_iterator as iterator type. Another approach would be
to write a "trait class" like this:

template<typename Container>
struct get_iterator_type
{typedef typename Container::iterator type;};

template<typename Container>
struct get_iterator_type<const Container>
{typedef typename Container::const_iterator type;};

template<class Container, class Functor, class Arg>
Functor for_all(Container & cont, Functor f, const Arg& arg)
{
typedef typename get_iterator_type<Container>::type iter_t;
iter_t beg = vec.begin();
iter_t const end = vec.end();
for (; beg!=end; ++beg){
func(*beg,arg);
}
return func;
}

Cheers!
SG
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,733
Messages
2,569,440
Members
44,830
Latest member
ZADIva7383

Latest Threads

Top