我想知道是否有人可以帮助我处理仿函数。我真的不明白函子是什么以及它们是如何工作的我试过用谷歌搜索它,但我仍然不明白。函子如何工作以及它们如何与模板一起工作
2 回答
函子基本上是一个“函数对象”。它是一个单独的函数,你已经包装在一个类或结构中,你可以将它传递给其他函数。
它们通过创建您自己的类或结构来工作,这些类或结构重载了函数调用运算符(称为 operator() )。通常,您只需将它作为参数就地构造它来创建它的一个实例,该函数采用函子。
假设您有以下内容:
std::vector<int> counts;
现在,您想要增加该向量中包含的所有计数。您可以手动循环它们以增加它们,或者您可以使用仿函数。在这种情况下,合适的仿函数如下所示:
struct IncrementFunctor
{
int operator() (int i)
{
return i + 1;
}
}
IncrementFunctor 现在是一个仿函数,它接受任何整数并递增它。要将其应用于计数,您可以使用 std::transform 函数,该函数将仿函数作为参数。
std::transform(
counts.begin(), // the start of the input range
counts.end(), // the end of the input range
counts.begin(), // the place where transform should place new values.
// in this case, we put it right back into the original list.
IncrementFunctor()); // an instance of your functor
语法 IncrementFunctor() 创建该函子的一个实例,然后将其直接传递给 std::transform。当然,您可以创建一个实例作为局部变量并传递它,但这更方便。
现在,进入模板。std::transform 中函子的类型是模板参数。这是因为 std::transform 不知道(或关心!)您的仿函数是哪种类型。它所关心的是它有一个合适的 operator() 定义,它可以做类似的事情
newValue = functor(oldValue);
编译器对模板非常聪明,并且通常可以自己弄清楚模板参数是什么。在这种情况下,编译器会自动意识到您传入了一个 IncrementFunctor 类型的参数,该参数在 std::transform 中定义为模板类型。它也对列表执行相同的操作,因此编译器会自动识别出实际调用如下所示:
std::transform<std::vector<int>::iterator, // type of the input iterator
std::vector<int>::iterator, // type of the output iterator
IncrementFunctor>( // type of your functor
counts.begin(), // the start of the input range
counts.end(), // the end of the input range
counts.begin(), // the place where transform should place new values.
// in this case, we put it right back into the original list.
IncrementFunctor()); // an instance of your functor
它为您节省了相当多的打字时间。;)
仿函数是可以使用函数调用运算符调用/调用的东西,在语法上通过附加()
,括号内可选参数列表。
这就是模板所需要的。就模板而言,调用 this 的东西是任何允许这种语法的东西——或者换句话说,是一个自由函数或一个覆盖operator()()
. (“免费”函数只是一个不是成员的函数,也就是说,它是全局范围内的函数或先前包含的命名空间范围内的函数。)
在模板元编程之外,我们通常不会说自由函数是一个仿函数,而是为覆盖的类的实例保留该名称operator()()
:
struct Foo {
public:
void operator()( int i ) { // do something }
void operator()( int i, char x ) { // do something else }
}
在 C++ 中,模板是编译的,所以只要语法有意义,编译器就会愉快地使用函数或仿函数:
template<typename T> class Bar {
private int j ;
public:
Bar( int i ) : j(i) {}
void doIt(T t) {
t( j ) ;
}
}
Foo f;
extern void resize( int i ) ; // in some header
Bar<Foo> bf( 5 ) ;
// a Bar that is templated on Foo
Bar< void (&)(int) > br( 5 ) ;
// a Bar that is templated on a function taking int and returning void
br.doit( &resize ) ; // call resize with br.j
bf.doit( f ) ; // call Foo::operator()(int) on Foo f with bf.j