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

发布于 2024-09-03 18:09:37 字数 322 浏览 3 评论 0原文

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

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

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?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

再浓的妆也掩不了殇 2024-09-10 18:09:37

您可以使用 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 博士的文章中介绍的解决方案< /a>.

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

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

瑾兮 2024-09-10 18:09:37

兰姆达来救援! (注意:需要 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); });

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

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

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

Or using decltype for the lambda parameter:

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); });

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);
}
巷雨优美回忆 2024-09-10 18:09:37

为什么不起作用

我希望编译器通过迭代器类型解析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 来成功,但需要您手动执行重载解析来“修复”正确的),我们想让编译器为我们工作。我们想要对某些参数调用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是自由函数还是成员函数,这都会起作用。

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.

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

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

Template deduction needs to select a type for UnaryFunction at the point of the call. But f doesn'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_each to aid the template deduction process by stating which f it wants, so template deduction simply fails. In order to have the template deduction succeed, you need to do more work on the call site.

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

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

百善笑为先 2024-09-10 18:09:37

这里的问题似乎不是重载解析,而是模板参数推导。虽然 @In silico 的 优秀答案 一般会解决模糊的重载问题,但在处理 < 时,这似乎是最好的解决方法code>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" );
}

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

// 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" );
}
回眸一笑 2024-09-10 18:09:37

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

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

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 should be 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.

许一世地老天荒 2024-09-10 18:09:37

不是为了回答你的问题,但我是唯一一个发现

for ( int i = 0; i < s.size(); i++ ) {
   f( s[i] );
}

比计算机在这种情况下建议的 for_each 替代方案更简单和更短的人吗?

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

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文