How do I specify a pointer to an overloaded function?

C++Stl

C++ Problem Overview


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?

C++ Solutions


Solution 1 - C++

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 - C++

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 - C++

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 fs 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 - C++

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 - C++

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" );
}

Solution 6 - C++

If you don't mind using C++11, here's a clever helper that is similar to (but less ugly than) the static cast:

template<class... Args, class T, class R>
auto resolve(R (T::*m)(Args...)) -> decltype(m)
{ return m; }

template<class T, class R>
auto resolve(R (T::*m)(void)) -> decltype(m)
{ return m; }

(Works for member functions; should be obvious how to modify it to work for freestanding functions, and you should be able to provide both versions and the compiler will select the right one for you.)

With thanks to Miro Knejp for suggesting: see also https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestiondavkaView Question on Stackoverflow
Solution 1 - C++In silicoView Answer on Stackoverflow
Solution 2 - C++milleniumbugView Answer on Stackoverflow
Solution 3 - C++BarryView Answer on Stackoverflow
Solution 4 - C++anonView Answer on Stackoverflow
Solution 5 - C++aldoView Answer on Stackoverflow
Solution 6 - C++MatthewView Answer on Stackoverflow