可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
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?
回答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.
回答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);
}
回答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.
回答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?
回答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\" );
}
回答6:
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.