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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2942426/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 11:34:30  来源:igfitidea点击:

How do I specify a pointer to an overloaded function?

c++stl

提问by davka

I want to pass an overloaded function to the std::for_each()algorithm. For example,

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

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?

我希望编译器能够f()通过迭代器类型进行解析。显然,它(GCC 4.1.2)没有这样做。那么,我该如何指定f()我想要的呢?

采纳答案by In silico

You can use static_cast<>()to specify which fto use according to the function signature implied by the function pointer type:

您可以根据函数指针类型所隐含的函数签名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)); 

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 fis a member function, then you need to use mem_fun, or for your case, use the solution presented in this Dr. Dobb's article.

如果f是成员函数,则您需要使用mem_fun,或者对于您的情况,请使用Dobb 博士的文章中提供的解决方案

回答by milleniumbug

Lambdas to the rescue! (note: C++11 required)

Lambda 来救援!(注意:需要 C++11)

std::for_each(s.begin(), s.end(), [&](char a){ return f(a); });

Or using decltype for the lambda parameter:

或者对 lambda 参数使用 decltype:

std::for_each(s.begin(), s.end(), [&](decltype(*s.begin()) a){ return f(a); });

With polymorphic lambdas (C++14):

使用多态 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);
}

回答by Barry

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.

我希望编译器能够f()通过迭代器类型进行解析。显然,它(gcc 4.1.2)没有这样做。

It'd be great if that were the case! However, for_eachis a function template, declared as:

如果真是这样就好了!然而,for_each是一个函数模板,声明为:

template <class InputIterator, class UnaryFunction>
UnaryFunction for_each(InputIterator, InputIterator, UnaryFunction );

Template deduction needs to select a type for UnaryFunctionat the point of the call. But fdoesn't have a specific type - it's an overloaded function, there are many fs each with different types. There is no current way for for_eachto aid the template deduction process by stating which fit wants, so template deduction simply fails. In order to have the template deduction succeed, you need to do more work on the call site.

模板推导需要UnaryFunction在调用时选择一种类型。但是f没有特定的类型——它是一个重载的函数,有很多fs,每个都有不同的类型。当前没有for_each方法通过说明f它想要的方式来帮助模板推导过程,因此模板推导完全失败。为了使模板推演成功,您需要在调用站点上做更多的工作。

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 fwe 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 fon some args. In the most generic way possible, that's:

几年后跳到这里,然后在 C++14 中跳来跳去。而不是使用 a static_cast(这将允许通过“修复”f我们想要使用的模板推导成功,但需要您手动进行重载解析以“修复”正确的),我们想让编译器为我们工作。我们想调用f一些参数。以最通用的方式,那就是:

[&](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 fitself and just do the right thing. This will work regardless of whether fis a free function or a member function.

这将完全符合您希望编译器所做的事情 - 对名称f本身执行重载解析,然后做正确的事情。无论f是自由函数还是成员函数,这都将起作用。

回答by Barry

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_eachalternative suggested by in silico in this case?

for_each在这种情况下,比in silico 建议的替代方案更简单和更短?

回答by aldo

The problem here seems to be not overload resolutionbut in fact template parameter deduction. While the excellent answerfrom @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:

这里的问题似乎不是重载解析,而是模板参数推导。虽然来自@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" );
}

回答by Matthew

If you don't mind using C++11, here's a clever helper that is similar to (but less ugly than) the static cast:

如果您不介意使用 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; }

(Works for member functions; should be obvious how to modify it to work for freestanding functions, and you shouldbe 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.

感谢 Miro Knejp 的建议:另见https://groups.google.com/a/isocpp.org/d/msg/std-discussion/rLVGeGUXsK0/IGj9dKmSyx4J