我如何指定一个重载函数的指针?
我想传递一个重载的函数到std::for_each()
algorithm。 例如,
class A { void f(char c); void f(int i); void scan(const std::string& s) { std::for_each(s.begin(), s.end(), f); } };
我希望编译器通过迭代器types来parsingf()
。 显然,它(GCC 4.1.2)不这样做。 那么,我怎样才能指定我想要哪个f()
?
您可以使用static_cast<>()
根据函数指针types所隐含的函数签名指定要使用的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博士的文章中提出的解决scheme 。
兰巴达去救援! (注意:需要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); });
使用多态lambdaexpression式(C ++ 14):
std::for_each(s.begin(), s.end(), [&](auto a){ return f(a); });
或消除重载消除(只适用于免费function):
void f_c(char i) { return f(i); } void scan(const std::string& s) { std::for_each(s.begin(), s.end(), f_c); }
为什么它不工作
我希望编译器通过迭代器types来parsing
f()
。 显然,它(海湾合作委员会4.1.2)不这样做。
如果是这样的话,那真是太好了! 但是, for_each
是一个函数模板,声明如下:
template <class InputIterator, class UnaryFunction> UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );
模板扣除需要在呼叫时为UnaryFunction
select一个types。 但是f
没有特定的types – 它是一个重载函数,每个types都有很多f
。 对于for_each
,目前还没有任何方法可以帮助模板扣除过程,因为它说明了它需要哪个f
,所以模板扣除就会失败。 为了使模板扣除成功,您需要在呼叫站点上做更多的工作。
通用的解决scheme来解决它
在这里跳了几年,C ++ 14以后。 我们不是使用static_cast
(这将允许通过“修复”我们想使用的模板来获得成功,但需要手动进行重载parsing来“修复”正确的),我们希望使编译器为我们工作。 我们想在一些参数上调用f
。 以最通用的方式,即:
[&](auto&&... args) -> decltype(auto) { return f(std::forward<decltype(args)>(args)...); }
键入的内容很多,但是这样的问题出现频繁,所以我们可以用macros(叹气)来包装它:
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(auto) { return func(std::forward<decltype(args)>(args)...); }
然后使用它:
void scan(const std::string& s) { std::for_each(s.begin(), s.end(), AS_LAMBDA(f)); }
这将完全符合你希望编译器所做的 – 对名称f
本身执行重载parsing,并做正确的事情。 无论f
是一个自由函数还是一个成员函数,这都可以工作。
P0119R1会允许你刚刚通过f
并且刚刚工作,但这在Oulu被拒绝了。 它可能在Issaquah以不同的forms回来,但它不会在C ++ 17中。
不回答你的问题,但我是唯一的发现
for ( int i = 0; i < s.size(); i++ ) { f( s[i] ); }
在这种情况下,它们是否比在这个计算机上提供的for_each
select更简单和更短?
这里的问题似乎不是重载决议,而是实际上模板参数的扣除 。 虽然@In silico的优秀答案通常会解决一个模糊的重载问题,但在处理std::for_each
(或类似的)时,似乎是明确指定其模板参数的最佳解决scheme:
// 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,下面是一个聪明的帮手,它和静态types相似(但不是那么丑陋):
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; }
(适用于成员函数;应该很明显如何修改它以适用于独立函数,而且您应该能够提供这两个版本,编译器将为您select合适的版本。)
感谢Miro Knejpbuild议:另请参阅https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J 。