Skip to main content
GameDev.net gamedev.net
🔒 Locked

std::for_each and function overloading

Started by titan_gd Jun 19, 2008 at 10:42 PM 8 replies 1.8k views
Original Post
titan_gd
titan_gd
How could I make this work? void foo( const char * ){} void foo( int ){} int main( int argc, char *argv[] ) { vector 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?
MaulingMonkey
MaulingMonkey
(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.
titan_gd
titan_gd
Thanks. static_cast(&Foo) seems to work , but that is certainly more obfuscated than it should be IMHO.
visitor
visitor
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 automaticallyfor_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.
MaulingMonkey
MaulingMonkey
Quote:
Original post by titan_gd
but that is certainly more obfuscated than it should be IMHO.


Welcome to C++, enjoy your stay [lol]
TheUnbeliever
TheUnbeliever
Quote:
Original post by titan_gd
Thanks. static_cast(&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)?
[TheUnbeliever]
MaulingMonkey
MaulingMonkey
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.
visitor
visitor
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 :) .)
Trienco
Trienco
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.
f@dzhttp://festini.device-zero.de
visitor
visitor
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).

Topic Locked

This topic has been locked by a moderator. New replies are not allowed.

Sign in to reply to this topic.