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

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

48825 次浏览

根据函数指针类型所暗示的函数签名,你可以使用 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,或者对于您的情况,使用 多布博士文章中提出的解决方案

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

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

既简单,短于 for_each的替代建议在计算机模拟在这种情况下?

这里的问题似乎不是 过载分辨率,而是 模板参数推导模板参数推导。虽然来自@In sio 的 答得好可以解决一个模糊的过载问题,但是在处理 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" );
}

拯救 Lambdas! (注意: 需要 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); });

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

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

为什么没用

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

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

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

模板演绎需要在调用点为 UnaryFunction选择一种类型。但是 f没有特定的类型——它是一个重载函数,有许多 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是空闲函数还是成员函数,这都将起作用。