Template function overloading question

J

Juha Nieminen

I tried to find tutorials about this in the web, but I couldn't find
any (every tutorial I found only gave a basic introduction to templates,
without going any deeper).

Assume that I have a class (let's call it A) with a (const) function
(taking no parameters) in it (let's say, for example, print()), a data
container containing instances of that class (let's call it v), and
that I want to create a function called ForEach() which I can call
like this:

ForEach(v, &A::print);

I believe the easiest way of implementing this ForEach() function
would be:

template<typename Container, typename Function>
void ForEach(const Container& c, Function f)
{
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*f)();
}
}

This works. The problem with it is that it can't be overloaded for
other purposes.
For example, assume that I want to make a version of the ForEach()
function which takes as second parameter a global/static function
which takes an A type const reference as parameter, so for example
if I had a function like "print(const A&)" I could do this:

ForEach(v, print);

In order to be able to overload ForEach() like this, the
implementation above would, AFAIK, have to use a more specific
version of the 'f' parameter than the generic 'Function' template
type. The Container element type would have to be specified
separately and the 'f' parameter of the function would have to
specify that it's a member function of that container element type
and not something else.

However, I just can't find the exact syntax for this anywhere.
Could somebody shed some light on this?
 
V

Victor Bazarov

Juha said:
I tried to find tutorials about this in the web, but I couldn't find
any (every tutorial I found only gave a basic introduction to
templates, without going any deeper).

Assume that I have a class (let's call it A) with a (const) function
(taking no parameters) in it (let's say, for example, print()), a data
container containing instances of that class (let's call it v), and
that I want to create a function called ForEach() which I can call
like this:

ForEach(v, &A::print);

I believe the easiest way of implementing this ForEach() function
would be:

template<typename Container, typename Function>
void ForEach(const Container& c, Function f)
{
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*f)();
}
}

This works. The problem with it is that it can't be overloaded for
other purposes.
For example, assume that I want to make a version of the ForEach()
function which takes as second parameter a global/static function
which takes an A type const reference as parameter, so for example
if I had a function like "print(const A&)" I could do this:

ForEach(v, print);

In order to be able to overload ForEach() like this, the
implementation above would, AFAIK, have to use a more specific
version of the 'f' parameter than the generic 'Function' template
type. The Container element type would have to be specified
separately and the 'f' parameter of the function would have to
specify that it's a member function of that container element type
and not something else.

However, I just can't find the exact syntax for this anywhere.
Could somebody shed some light on this?

I believe the common way to do that is to use not a member function
call, but a functor, and pass the container element to it:

template<class C, class F>
void ForEach(C c, F f) {
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
f(*iter);
}
}

and when you need to use a member function, use binders (see
<functional> and 'bind1st', 'bind2nd', etc)

V
 
J

Juha Nieminen

Victor said:
I believe the common way to do that is to use not a member function
call, but a functor, and pass the container element to it:

template<class C, class F>
void ForEach(C c, F f) {
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
f(*iter);
}
}

and when you need to use a member function, use binders (see
<functional> and 'bind1st', 'bind2nd', etc)

That didn't really answer my question.
 
V

Victor Bazarov

Juha said:
That didn't really answer my question.

OK, sorry about that. I thought you needed a generic commonly used
solution for doing 'ForEach' stuff, and you are just asking for the
syntax... My bad.

Here you go

template<class C, class T>
void ForEach(C c, void (T::*member)() const)
{
for(typename C::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*member)();
}
}


template<class C>
void ForEach(C c, void (*fptr)(typename C::value_type const&))
{
for(typename C::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
fptr(*iter);
}
}

#include <vector>
struct A
{
void print() const {}
};

void print(A const&) {}

int main()
{
std::vector<A> va;
ForEach(va, &A::print);
ForEach(va, print);
}

Does *that* answer your question?

V
 
A

Alp Mestan

I believe the common way to do that is to use not a member function
call, but a functor, and pass the container element to it:

It is the way i use to do, and i read many c++ source codes using this
way.
template<class C, class F>
void ForEach(C c, F f) {
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
f(*iter);
}
}

and when you need to use a member function, use binders (see
<functional> and 'bind1st', 'bind2nd', etc)
See boost.bind also for this task.

Are you writing ForEach for learning ? If you don't, use std::foreach
(<algorithm>).

Alp
 
J

Juha Nieminen

Victor said:
template<class C, class T>
void ForEach(C c, void (T::*member)() const)
{
for(typename C::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*member)();
}
}

I wonder if that T could be removed like this:

template<typename Container>
void ForEach(const Container& c,
void (Container::value_type::*member)() const)
{
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*member)();
}
}

It seems to work.
It didn't occur to me to use ::value_type in order to easily get
that type. I thought it would be necessary to specify that type as
a specific template parameter.

The return value of the function is fixed to void, though. Would
this be the "standard" way of removing that requirement?

template<typename Container, typename Ret>
void ForEach(const Container& c,
Ret (Container::value_type::*member)() const)
{
for(typename Container::const_iterator iter = c.begin();
iter != c.end(); ++iter)
{
((*iter).*member)();
}
}
 
J

Juha Nieminen

Alp said:
Are you writing ForEach for learning ?

Mostly yes.
If you don't, use std::foreach
(<algorithm>).

It has always bothered me why std::for_each cannot have an overloaded
version which takes an entire data structure as parameter so that you
don't always have to specify "v.begin(), v.end()" each time you want
to do something to the entire data structure.

I also wonder that given that it's possible to make a version of
for_each which takes a member function directly why do you have to
use a longer syntax to do that?
In other words, why couldn't std::for_each() be overloaded so that
you could do: std::for_each(v, &A::print); instead of having to do:
std::for_each(v.begin(), v.end(), std::mem_fun_ref(&A::print));
which is much longer and more tedious to write, and requires you
to remember the more complicated syntax for that, as well as to add
an additional #include for it to work.

Other algorithms could perfectly work in the same way too. Besides
having the regular begin/end version, they could have a shorter version,
like: std::sort(v, &A::isGreaterThan); Currently you have to write:
std::sort(v.begin(), v.end(), std::mem_fun_ref(&A::isGreaterThan));
 
V

Victor Bazarov

Juha said:
Mostly yes.


It has always bothered me why std::for_each cannot have an overloaded
version which takes an entire data structure as parameter so that you
don't always have to specify "v.begin(), v.end()" each time you want
to do something to the entire data structure.

What prevents you from providing a simple wrapper for it?
I also wonder that given that it's possible to make a version of
for_each which takes a member function directly why do you have to
use a longer syntax to do that?

Because the point of the library is to provide the *minimal* set of
tools, which when combined can form new, more convenient ones.

V
 
J

Juha Nieminen

Victor said:
What prevents you from providing a simple wrapper for it?

Quoting the other poster: "Are you writing ForEach for learning ?
If you don't, use std::foreach (<algorithm>)."
 

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,770
Messages
2,569,583
Members
45,074
Latest member
StanleyFra

Latest Threads

Top