Как указать указатель на перегруженную функцию?
Я хочу передать перегруженную функцию алгоритму 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.