如何指定指向重载函数的指针?

时间:2010-05-31 08:55:22

标签: c++ stl

我想将重载函数传递给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()

6 个答案:

答案 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