How do I specify a pointer to an overloaded function?
I want to pass an overloaded function to the std::for_each()
algorithm. For example,
class A {
void f(char c);
void f(int i);
void scan(const std::string& s) {
std::for_each(s.begin(), s.end(), f);
}
};
I'd expect the compiler to resolve f()
by the iterator type. Apparently, it (GCC 4.1.2) doesn't do it. So, how can I specify which f()
I want?
Solution 1:
You can use static_cast<>()
to specify which f
to use according to the function signature implied by the function pointer type:
// Uses the void f(char c); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(char)>(&f));
// Uses the void f(int i); overload
std::for_each(s.begin(), s.end(), static_cast<void (*)(int)>(&f));
Or, you can also do this:
// The compiler will figure out which f to use according to
// the function pointer declaration.
void (*fpc)(char) = &f;
std::for_each(s.begin(), s.end(), fpc); // Uses the void f(char c); overload
void (*fpi)(int) = &f;
std::for_each(s.begin(), s.end(), fpi); // Uses the void f(int i); overload
If f
is a member function, then you need to use mem_fun
, or for your case, use the solution presented in this Dr. Dobb's article.
Solution 2:
Lambdas to the rescue! (note: C++11 required)
std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });
Or using decltype for the lambda parameter:
std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });
With polymorphic lambdas (C++14):
std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); });
Or disambiguate by removing overloading (only works for free functions):
void f_c(char i)
{
return f(i);
}
void scan(const std::string& s)
{
std::for_each(s.begin(), s.end(), f_c);
}
Solution 3:
Why doesn't it work
I'd expect the compiler to resolve
f()
by the iterator type. Apparently, it (gcc 4.1.2) doesn't do it.
It'd be great if that were the case! However, for_each
is a function template, declared as:
template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );
Template deduction needs to select a type for UnaryFunction
at the point of the call. But f
doesn't have a specific type - it's an overloaded function, there are many f
s each with different types. There is no current way for for_each
to aid the template deduction process by stating which f
it wants, so template deduction simply fails. In order to have the template deduction succeed, you need to do more work on the call site.
Generic solution to fixing it
Hopping in here a few years and C++14 later. Rather than use a static_cast
(which would allow template deduction to succeed by "fixing" which f
we want to use, but requires you to manually do overload resolution to "fix" the correct one), we want to make the compiler work for us. We want to call f
on some args. In the most generic way possible, that's:
[&](auto&&... args) -> decltype(auto) { return f(std::forward<decltype(args)>(args)...); }
That's a lot to type, but this sort of problem comes up annoyingly frequently, so we can just wrap that in a macro (sigh):
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(func(std::forward<decltype(args)>(args)...)) { return func(std::forward<decltype(args)>(args)...); }
and then just use it:
void scan(const std::string& s) {
std::for_each(s.begin(), s.end(), AS_LAMBDA(f));
}
This will do exactly what you wish the compiler did - perform overload resolution on the name f
itself and just do the right thing. This will work regardless of whether f
is a free function or a member function.
Solution 4:
Not to answer your question, but am I the only one that finds
for ( int i = 0; i < s.size(); i++ ) {
f( s[i] );
}
both simpler and shorter than the for_each
alternative suggested by in silico in this case?
Solution 5:
The problem here seems to be not overload resolution but in fact template parameter deduction. While the excellent answer from @In silico will solve an ambiguous overloading problem in general, it seems the best fix when dealing with std::for_each
(or similar) is to explicitly specify its template parameters:
// Simplified to use free functions instead of class members.
#include <algorithm>
#include <iostream>
#include <string>
void f( char c )
{
std::cout << c << std::endl;
}
void f( int i )
{
std::cout << i << std::endl;
}
void scan( std::string const& s )
{
// The problem:
// error C2914: 'std::for_each' : cannot deduce template argument as function argument is ambiguous
// std::for_each( s.begin(), s.end(), f );
// Excellent solution from @In silico (see other answer):
// Declare a pointer of the desired type; overload resolution occurs at time of assignment
void (*fpc)(char) = f;
std::for_each( s.begin(), s.end(), fpc );
void (*fpi)(int) = f;
std::for_each( s.begin(), s.end(), fpi );
// Explicit specification (first attempt):
// Specify template parameters to std::for_each
std::for_each< std::string::const_iterator, void(*)(char) >( s.begin(), s.end(), f );
std::for_each< std::string::const_iterator, void(*)(int) >( s.begin(), s.end(), f );
// Explicit specification (improved):
// Let the first template parameter be derived; specify only the function type
std::for_each< decltype( s.begin() ), void(*)(char) >( s.begin(), s.end(), f );
std::for_each< decltype( s.begin() ), void(*)(int) >( s.begin(), s.end(), f );
}
void main()
{
scan( "Test" );
}