Jump to content
  • Advertisement
Sign in to follow this  
titan_gd

std::for_each and function overloading

This topic is 3707 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

How could I make this work? void foo( const char * ){} void foo( int ){} int main( int argc, char *argv[] ) { vector<int> ints; for_each( ints.begin(), ints.end(), &foo ); return 0; } It seems to me the compiler really has all the info it needs to figure out which version of foo it should call? Is there a trick I could do to make this work?

Share this post


Link to post
Share on other sites
Advertisement
(void (*)(int))&foo

Turning overloaded functions into functors is one of those rough edges in C++. A more complex implementation of for_each could do what you want, in theory.

Share this post


Link to post
Share on other sites
Thanks. static_cast<void (*)(int)>(&Foo) seems to work , but that is certainly more obfuscated than it should be IMHO.

Share this post


Link to post
Share on other sites
I have never had to deal with this but the following might work:

for_each( ints.begin(), ints.end(), ptr_fun<int, void>(foo) );
//or void return type can be deduced automatically
for_each( ints.begin(), ints.end(), ptr_fun<int>(foo) );


std::ptr_fun in the functional header is a function that wraps your function into a function object (this allows you to combine you function into more complex statements, e.g not1(foo) isn't directly possible).

The template arguments are argument type(s) and return type. Normally they can be automatically detected, but there is no reason why you couldn't direct the compiler in more ambigous situations.

Share this post


Link to post
Share on other sites
Quote:
Original post by titan_gd
Thanks. static_cast<void (*)(int)>(&Foo) seems to work , but that is certainly more obfuscated than it should be IMHO.


As an aside, can someone clarify precisely why this works? It is just because there is no valid cast from void (*)(const char*) to void (*)(int)?

Share this post


Link to post
Share on other sites
Quote:
Original post by titan_gd
As an aside, can someone clarify precisely why this works? It is just because there is no valid cast from void (*)(const char*) to void (*)(int)?

The problem is that foo is overloaded. There are multiple functions with the same name, yet with different signatures. There's a void foo( int ) {}, which is what we want, but because std::for_each is stupid, it doesn't know which of the overloaded functions we want. Due to the nature of C++ we can't just forward the overloaded function directly for it to be determined later, we have to pick a type now and stick with it.

&foo can be assigned to a void (*)(int) without casting, the cast is simply to select a type.

A smarter version of for_each might provide some overloads:

template < typename Iterator, typename R > void for_each( Iterator begin, Iterator end, R (*f)( typename std::iterator_traits<Iterator>::value_type ) ) {
for_each( begin, end, std::mem_fun(f) );
}

template < typename Iterator, typename R > void for_each( Iterator begin, Iterator end, R (*f)( typename std::iterator_traits<Iterator>::reference ) ) {
for_each( begin, end, std::mem_fun(f) );
}

template < typename Iterator, typename R > void for_each( Iterator begin, Iterator end, R (*f)( typename std::iterator_traits<Iterator>::const_reference ) ) {
for_each( begin, end, std::mem_fun(f) );
}



This would let the compiler figure out which version of foo to use on it's own, at the call site of for_each -- but this quickly gets more and more complex, especially with the other algorithms that pass their functor more than one parameter (9 overloads just to support by-value, by-reference, and by-const-reference for each argument, not counting anything else we might need to do!). It also doesn't help with any functors directly used themselves, since they can't infer the signatures they're supposed to look for from the iterators passed.

Share this post


Link to post
Share on other sites
Templates are tricky but when deducing template arguments for a function call the compiler should go for the closest match.

However, the signature of for_each is

template<class InIt, class Fn1>
Fn1 for_each(InIt first, InIt last, Fn1 func);


As you can see the type of the third parameter is completely unrelated to the type of the first two. Therefore the compiler cannot deduce which of the two overloads would be a closer match: both are equally OK.

With the cast you would tell the compiler explicitly the type of the third argument and it would pick the one overload that matches it.

Alternatively to other proposed solutions you might also simply tell the compiler the types yourself:

typedef void(*int_fun)(int);
std::for_each<std::vector<int>::iterator, int_fun>(vec.begin(), vec.end(), foo);


(I would probably go with the ptr_fun suggestion since it is the only one that doesn't involve the ugly function pointer syntax, and has a fun name :) .)

Share this post


Link to post
Share on other sites
Any reason why the functions have to be passed as functions? It's a lot less annoying and obfuscated to just give for_each a functor. Alternatively specify the template parameters for for_each.


struct foo
{
void operator()( const char * ) {}
void operator()( int ) {}
};

int main() {
vector<int> ints;
for_each( ints.begin(), ints.end(), foo() );
return 0;
}

with overloaded functions:
for_each<vector<int>::iterator, void(*)(int)>( ints.begin(), ints.end(), foo );


Though generally I haven't stumbled over a situation yet, where all the hassle to use for_each over a loop was worth it, anyway.

Share this post


Link to post
Share on other sites
Yet another alternative is to get boost and use BOOST_FOREACH:


BOOST_FOREACH(int i, vec){
foo(i);
}


It seems that C++0x might support this syntax natively (making for_each practically unneeded, IMO).

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

We are the game development community.

Whether you are an indie, hobbyist, AAA developer, or just trying to learn, GameDev.net is the place for you to learn, share, and connect with the games industry. Learn more About Us or sign up!

Sign me up!