我想将重载函数传递给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()
?
答案 0 :(得分:125)
您可以使用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
,或者根据您的情况使用the solution presented in this Dr. Dobb's article。
答案 1 :(得分:24)
Lambdas救援! (注意:需要C ++ 11)
std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });
或者使用decltype作为lambda参数:
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); });
通过删除重载消除歧义(仅适用于免费功能):
void f_c(char i)
{
return f(i);
}
void scan(const std::string& s)
{
std::for_each(s.begin(), s.end(), f_c);
}
答案 2 :(得分:15)
我希望编译器通过迭代器类型解析
f()
。显然,它(gcc 4.1.2)没有这样做。
如果是这样的话会很棒!但是,for_each
是一个函数模板,声明为:
template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );
模板推导需要在通话时选择UnaryFunction
的类型。但f
没有特定类型 - 它是一个重载函数,有许多f
,每个都有不同的类型。 for_each
目前没有办法通过说明它需要哪个f
来帮助模板扣除过程,因此模板扣除完全失败。为了使模板扣除成功,您需要在呼叫站点上做更多工作。
在这里跳了几年,然后是C ++ 14。而不是使用static_cast
(通过“修复”我们想要使用的f
来允许模板推断成功,但是需要您手动执行重载决策以“修复”正确的一个),我们想让编译器为我们工作。我们想在某些args上调用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
是自由函数还是成员函数,这都将起作用。
答案 3 :(得分:4)
不回答你的问题,但我是唯一一个找到
的人for ( int i = 0; i < s.size(); i++ ) {
f( s[i] );
}
在这种情况下,比计算机中提出的for_each
替代方案更简单和更短?
答案 4 :(得分:4)
此处的问题似乎不是重载决策,而实际上是模板参数推断。虽然来自@In silico的excellent answer将解决一般模糊的重载问题,但处理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" );
}
答案 5 :(得分:4)
如果您不介意使用C ++ 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。