我想将重载函数传递给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()
我想要的呢?
我想将重载函数传递给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()
我想要的呢?
Answers:
您可以根据函数指针类型所隐含的函数签名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
,或者针对您的情况,使用Dobb博士文章中提供的解决方案。
f()
是一个班级的成员(请参见上面的编辑示例)
reinterpret_cast
。我最经常看到用于此目的的C样式转换。我的规则是,对函数指针进行强制转换是危险且不必要的(如第二个代码片段所示,存在隐式转换)。
std::for_each(s.begin(), s.end(), static_cast<void (A::*)(char)>(&A::f));
Lambda救援!(注意:需要C ++ 11)
std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });
或对lambda参数使用decltype:
std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });
使用多态lambda(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);
}
mem_fn
和bind
,而且BTW也是C ++ 11)。另外,如果我们想得到真正的书呆子,[&](char a){ return f(a); }
则为28个字符,static_cast<void (A::*)(char)>(&f)
为35个字符。
我希望编译器
f()
按迭代器类型解析。显然,它(gcc 4.1.2)没有做到这一点。
如果真是那样,那就太好了!但是,for_each
是一个函数模板,声明为:
template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );
模板推导需要UnaryFunction
在调用时选择一种类型。但是f
没有特定的类型-这是一个重载函数,有许多f
s都有不同的类型。目前尚无办法for_each
说明模板推导过程f
,因此模板推导完全失败。为了使模板推导成功,您需要在呼叫站点上做更多的工作。
跳入这里几年,然后C ++ 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
是自由函数还是成员函数,这都将起作用。
不是回答您的问题,而是我是唯一找到的人
for ( int i = 0; i < s.size(); i++ ) {
f( s[i] );
}
for_each
在这种情况下,比in silico建议的替代方案更简单,更短?
这里的问题似乎不是过载解析,而是实际上模板参数的推导。虽然@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" );
}
如果您不介意使用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。
R
,所以不会被推导。在此答案中也没有提及。
R
,我正在提供Args
。R
并被T
推导。确实,答案可以得到改善。(T
尽管在我的示例中没有,因为它不是指向成员的指针,因为它不适用于std::for_each
。)