Как указать указатель на перегруженную функцию?

Я хочу передать перегруженную функцию алгоритму std::for_each(). Например,

class A {
    void f(char c);
    void f(int i);

    void scan(const std::string& s) {
        std::for_each(s.begin(), s.end(), f);
    }
};

Я ожидаю, что компилятор решит f() по типу итератора. По-видимому, он (GCC 4.1.2) этого не делает. Итак, как я могу указать, какой f() я хочу?

Ответ 1

Вы можете использовать static_cast<>(), чтобы указать, какой f использовать в соответствии с сигнатурой функции, подразумеваемой типом указателя функции:

// 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)); 

Или вы также можете сделать это:

// 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

Если f является функцией-членом, вам нужно использовать mem_fun, или для вашего случая используйте решение, представленное в этой статье доктора Добба.

Ответ 2

Лямбдас на помощь! (обратите внимание: требуется С++ 11)

std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });

Или используя параметр decltype для параметра лямбда:

std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });

С полиморфными лямбдами (С++ 14):

std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); });

Или устранить проблему, удалив перегрузку (работает только для бесплатных функций):

void f_c(char i)
{
    return f(i);
}

void scan(const std::string& s)
{
    std::for_each(s.begin(), s.end(), f_c);
}

Ответ 3

Почему это не работает

Я ожидал, что компилятор разрешит f() по типу итератора. По-видимому, он (gcc 4.1.2) этого не делает.

Было бы здорово, если бы это было так! Однако for_each является шаблоном функции, объявленным как:

template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );

UnaryFunction шаблона должен выбрать тип для UnaryFunction в точке вызова. Но f не имеет определенного типа - это перегруженная функция, существует много f с разными типами. Там в настоящее время нет способа для for_each для облегчения процесса дедукции шаблона, заявив, который f он хочет, так шаблон вычет просто не удается. Чтобы добиться успеха в шаблоне, вам нужно сделать больше работы на сайте вызова.

Общее решение для его фиксации

Здесь выплюнул несколько лет и С++ 14 позже. Вместо того, чтобы использовать static_cast (который позволил бы шаблон вычет, чтобы преуспеть на "фиксации", который f мы хотим использовать, но требует, чтобы вы вручную делать разрешение перегрузки "установить" правильный), мы хотим, чтобы сделать работу компилятора для нас, Мы хотим называть f на некоторых аргументах. В наиболее общем виде это:

[&](auto&&... args) -> decltype(auto) { return f(std::forward<decltype(args)>(args)...); }

Это много, чтобы печатать, но такая проблема возникает раздражающе часто, поэтому мы можем просто обернуть это макросом (вздох):

#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(func(std::forward<decltype(args)>(args)...)) { return func(std::forward<decltype(args)>(args)...); }

а затем просто используйте его:

void scan(const std::string& s) {
    std::for_each(s.begin(), s.end(), AS_LAMBDA(f));
}

Это будет делать именно то, что вы пожелаете компилятору - выполнить разрешение перегрузки на самом имени f и просто поступать правильно. Это будет работать независимо от того, является ли f свободной функцией или функцией-членом.

Ответ 4

Не отвечать на ваш вопрос, но я единственный, кто находит

for ( int i = 0; i < s.size(); i++ ) {
   f( s[i] );
}

и проще и короче альтернативы for_each, предложенной в silico в этом случае?

Ответ 5

Проблема здесь, по-видимому, не в разрешении перегрузки, а на самом деле вычитании параметра шаблона. В то время как отличный ответ от @In silico решит проблему неоднозначной перегрузки в целом, кажется лучшим решением при работе с std::for_each (или аналогичным) является явно указать параметры своего шаблона:

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

Ответ 6

Если вы не против использования С++ 11, здесь умный помощник, похожий на (но менее уродливый, чем) статический приведение:

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

(Работает для функций-членов, должно быть очевидно, как изменить его для работы для автономных функций, и вы должны иметь возможность предоставить обе версии, а компилятор выберет для вас правильный вариант.)

С благодарностью Miro Knejp за предложение: см. также https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J.