什么是 C++ 函子及其用途?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/356950/
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
What are C++ functors and their uses?
提问by Konrad
I keep hearing a lot about functors in C++. Can someone give me an overview as to what they are and in what cases they would be useful?
我一直听到很多关于 C++ 中的函子的消息。有人能给我一个关于它们是什么以及它们在什么情况下有用的概述吗?
回答by jalf
A functor is pretty much just a class which defines the operator(). That lets you create objects which "look like" a function:
函子几乎只是一个定义 operator() 的类。这使您可以创建“看起来像”函数的对象:
// this is a functor
struct add_x {
add_x(int val) : x(val) {} // Constructor
int operator()(int y) const { return x + y; }
private:
int x;
};
// Now you can use it like this:
add_x add42(42); // create an instance of the functor class
int i = add42(8); // and "call" it
assert(i == 50); // and it added 42 to its argument
std::vector<int> in; // assume this contains a bunch of values)
std::vector<int> out(in.size());
// Pass a functor to std::transform, which calls the functor on every element
// in the input sequence, and stores the result to the output sequence
std::transform(in.begin(), in.end(), out.begin(), add_x(1));
assert(out[i] == in[i] + 1); // for all i
There are a couple of nice things about functors. One is that unlike regular functions, they can contain state. The above example creates a function which adds 42 to whatever you give it. But that value 42 is not hardcoded, it was specified as a constructor argument when we created our functor instance. I could create another adder, which added 27, just by calling the constructor with a different value. This makes them nicely customizable.
函子有一些不错的地方。一是与常规函数不同,它们可以包含状态。上面的例子创建了一个函数,它把你给的任何东西都加上 42。但是值 42 不是硬编码的,它在我们创建函子实例时被指定为构造函数参数。我可以创建另一个加法器,它添加了 27,只需调用具有不同值的构造函数即可。这使得它们可以很好地定制。
As the last lines show, you often pass functors as arguments to other functions such as std::transform or the other standard library algorithms. You could do the same with a regular function pointer except, as I said above, functors can be "customized" because they contain state, making them more flexible (If I wanted to use a function pointer, I'd have to write a function which added exactly 1 to its argument. The functor is general, and adds whatever you initialized it with), and they are also potentially more efficient. In the above example, the compiler knows exactly which function std::transform
should call. It should call add_x::operator()
. That means it can inline that function call. And that makes it just as efficient as if I had manually called the function on each value of the vector.
如最后几行所示,您经常将函子作为参数传递给其他函数,例如 std::transform 或其他标准库算法。您可以对常规函数指针执行相同操作,但正如我上面所说,函子可以“定制”,因为它们包含状态,从而使它们更加灵活(如果我想使用函数指针,我必须编写一个函数它在它的参数中正好加了 1。函子是通用的,并添加了你初始化它的任何东西),而且它们也可能更有效。在上面的例子中,编译器确切地知道std::transform
应该调用哪个函数。它应该调用add_x::operator()
. 这意味着它可以内联该函数调用。这使得它就像我在向量的每个值上手动调用函数一样高效。
If I had passed a function pointer instead, the compiler couldn't immediately see which function it points to, so unless it performs some fairly complex global optimizations, it'd have to dereference the pointer at runtime, and then make the call.
如果我传递了一个函数指针,编译器无法立即看到它指向哪个函数,因此除非它执行一些相当复杂的全局优化,否则它必须在运行时取消引用该指针,然后进行调用。
回答by Evgeny Lazin
Little addition. You can use boost::function
, to create functors from functions and methods, like this:
小补充。您可以使用boost::function
, 从函数和方法创建函子,如下所示:
class Foo
{
public:
void operator () (int i) { printf("Foo %d", i); }
};
void Bar(int i) { printf("Bar %d", i); }
Foo foo;
boost::function<void (int)> f(foo);//wrap functor
f(1);//prints "Foo 1"
boost::function<void (int)> b(&Bar);//wrap normal function
b(1);//prints "Bar 1"
and you can use boost::bind to add state to this functor
你可以使用 boost::bind 向这个函子添加状态
boost::function<void ()> f1 = boost::bind(foo, 2);
f1();//no more argument, function argument stored in f1
//and this print "Foo 2" (:
//and normal function
boost::function<void ()> b1 = boost::bind(&Bar, 2);
b1();// print "Bar 2"
and most useful, with boost::bind and boost::function you can create functor from class method, actually this is a delegate:
最有用的是,使用 boost::bind 和 boost::function 你可以从类方法创建函子,实际上这是一个委托:
class SomeClass
{
std::string state_;
public:
SomeClass(const char* s) : state_(s) {}
void method( std::string param )
{
std::cout << state_ << param << std::endl;
}
};
SomeClass *inst = new SomeClass("Hi, i am ");
boost::function< void (std::string) > callback;
callback = boost::bind(&SomeClass::method, inst, _1);//create delegate
//_1 is a placeholder it holds plase for parameter
callback("useless");//prints "Hi, i am useless"
You can create list or vector of functors
您可以创建函子列表或向量
std::list< boost::function<void (EventArg e)> > events;
//add some events
....
//call them
std::for_each(
events.begin(), events.end(),
boost::bind( boost::apply<void>(), _1, e));
There is one problem with all this stuff, compiler error messages is not human readable :)
所有这些东西都有一个问题,编译器错误消息不是人类可读的:)
回答by James Curran
A Functor is a object which acts like a function.
Basically, a class which defines operator()
.
Functor 是一个像函数一样的对象。基本上,一个定义operator()
.
class MyFunctor
{
public:
int operator()(int x) { return x * 2;}
}
MyFunctor doubler;
int x = doubler(5);
The real advantage is that a functor can hold state.
真正的优势是函子可以保持状态。
class Matcher
{
int target;
public:
Matcher(int m) : target(m) {}
bool operator()(int x) { return x == target;}
}
Matcher Is5(5);
if (Is5(n)) // same as if (n == 5)
{ ....}
回答by doc
Name "functor" has been traditionaly used in category theorylong before C++ appeared on the scene. This has nothing to do with C++ concept of functor. It's better to use name function objectinstead of what we call "functor" in C++. This is how other programming languages call similar constructs.
早在 C++ 出现之前,“函子”这个名称就已经被传统地用于范畴论中。这与函子的 C++ 概念无关。最好使用名称函数对象,而不是我们在 C++ 中所说的“函子”。这就是其他编程语言如何调用类似结构。
Used instead of plain function:
用于代替普通函数:
Features:
特征:
- Function object may have state
- Function object fits into OOP (it behaves like every other object).
- 函数对象可能有状态
- 函数对象适合 OOP(它的行为与其他所有对象一样)。
Cons:
缺点:
- Brings more complexity to the program.
- 给程序带来了更多的复杂性。
Used instead of function pointer:
用于代替函数指针:
Features:
特征:
- Function object often may be inlined
- 函数对象通常可以被内联
Cons:
缺点:
- Function object can not be swapped with other function object type during runtime (at least unless it extends some base class, which therefore gives some overhead)
- 函数对象在运行时不能与其他函数对象类型交换(至少除非它扩展了某个基类,因此会产生一些开销)
Used instead of virtual function:
代替虚函数使用:
Features:
特征:
- Function object (non-virtual) doesn't require vtable and runtime dispatching, thus it is more efficient in most cases
- 函数对象(非虚拟)不需要 vtable 和运行时分派,因此在大多数情况下效率更高
Cons:
缺点:
- Function object can not be swapped with other function object type during runtime (at least unless it extends some base class, which therefore gives some overhead)
- 函数对象在运行时不能与其他函数对象类型交换(至少除非它扩展了某个基类,因此会产生一些开销)
回答by Matthew Crumley
Like others have mentioned, a functor is an object that acts like a function, i.e. it overloads the function call operator.
就像其他人提到的那样,函子是一个对象,其行为类似于函数,即它重载了函数调用运算符。
Functors are commonly used in STL algorithms. They are useful because they can hold state before and between function calls, like a closure in functional languages. For example, you could define a MultiplyBy
functor that multiplies its argument by a specified amount:
函子通常用于 STL 算法。它们很有用,因为它们可以在函数调用之前和之间保持状态,就像函数式语言中的闭包一样。例如,您可以定义一个MultiplyBy
将其参数乘以指定数量的函子:
class MultiplyBy {
private:
int factor;
public:
MultiplyBy(int x) : factor(x) {
}
int operator () (int other) const {
return factor * other;
}
};
Then you could pass a MultiplyBy
object to an algorithm like std::transform:
然后你可以将一个MultiplyBy
对象传递给像 std::transform 这样的算法:
int array[5] = {1, 2, 3, 4, 5};
std::transform(array, array + 5, array, MultiplyBy(3));
// Now, array is {3, 6, 9, 12, 15}
Another advantage of a functor over a pointer to a function is that the call can be inlined in more cases. If you passed a function pointer to transform
, unless thatcall got inlined and the compiler knows that you always pass the same function to it, it can't inline the call through the pointer.
与指向函数的指针相比,函子的另一个优点是可以在更多情况下内联调用。如果您将函数指针传递给transform
,除非该调用被内联并且编译器知道您总是将相同的函数传递给它,否则它无法通过指针内联调用。
回答by Johanne Irish
For the newbies like me among us: after a little research I figured out what the code jalf posted did.
对于我们中间像我这样的新手:经过一些研究,我弄清楚了 jalf 发布的代码做了什么。
A functor is a class or struct object which can be "called" like a function. This is made possible by overloading the () operator
. The () operator
(not sure what its called) can take any number of arguments. Other operators only take two i.e. the + operator
can only take two values (one on each side of the operator) and return whatever value you have overloaded it for. You can fit any number of arguments inside a () operator
which is what gives it its flexibility.
函子是一个类或结构对象,可以像函数一样被“调用”。这是通过重载() operator
. 的() operator
(不知道其所谓的)可以采取任何数量的参数。其他运算符只取两个值,即+ operator
只能取两个值(运算符的每一侧一个)并返回您为其重载的任何值。您可以在 a 中放入任意数量的参数() operator
,这为其提供了灵活性。
To create a functor first you create your class. Then you create a constructor to the class with a parameter of your choice of type and name. This is followed in the same statement by an initializer list (which uses a single colon operator, something I was also new to) which constructs the class member objects with the previously declared parameter to the constructor. Then the () operator
is overloaded. Finally you declare the private objects of the class or struct you have created.
要首先创建函子,您需要创建您的类。然后使用您选择的类型和名称的参数为类创建一个构造函数。在同一个语句中,后面跟着一个初始化列表(它使用单个冒号运算符,我也是新手),它使用先前声明的构造函数参数构造类成员对象。然后() operator
是超载。最后声明您创建的类或结构的私有对象。
My code (I found jalf's variable names confusing)
我的代码(我发现 jalf 的变量名令人困惑)
class myFunctor
{
public:
/* myFunctor is the constructor. parameterVar is the parameter passed to
the constructor. : is the initializer list operator. myObject is the
private member object of the myFunctor class. parameterVar is passed
to the () operator which takes it and adds it to myObject in the
overloaded () operator function. */
myFunctor (int parameterVar) : myObject( parameterVar ) {}
/* the "operator" word is a keyword which indicates this function is an
overloaded operator function. The () following this just tells the
compiler that () is the operator being overloaded. Following that is
the parameter for the overloaded operator. This parameter is actually
the argument "parameterVar" passed by the constructor we just wrote.
The last part of this statement is the overloaded operators body
which adds the parameter passed to the member object. */
int operator() (int myArgument) { return myObject + myArgument; }
private:
int myObject; //Our private member object.
};
If any of this is inaccurate or just plain wrong feel free to correct me!
如果其中任何一个不准确或完全错误,请随时纠正我!
回答by Paul Fultz II
A functor is a higher-order functionthat applies a function to the parametrized(ie templated) types. It is a generalization of the maphigher-order function. For example, we could define a functor for std::vector
like this:
函子是将函数应用于参数化(即模板化)类型的高阶函数。它是map高阶函数的推广。例如,我们可以std::vector
像这样定义一个函子:
template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::vector<U> fmap(F f, const std::vector<T>& vec)
{
std::vector<U> result;
std::transform(vec.begin(), vec.end(), std::back_inserter(result), f);
return result;
}
This function takes a std::vector<T>
and returns std::vector<U>
when given a function F
that takes a T
and returns a U
. A functor doesn't have to be defined over container types, it can be defined for any templated type as well, including std::shared_ptr
:
当给定一个接受 a并返回 a的函数时,此函数接受 astd::vector<T>
并返回。函子不必在容器类型上定义,它也可以为任何模板类型定义,包括:std::vector<U>
F
T
U
std::shared_ptr
template<class F, class T, class U=decltype(std::declval<F>()(std::declval<T>()))>
std::shared_ptr<U> fmap(F f, const std::shared_ptr<T>& p)
{
if (p == nullptr) return nullptr;
else return std::shared_ptr<U>(new U(f(*p)));
}
Heres a simple example that converts the type to a double
:
这是一个将类型转换为 a 的简单示例double
:
double to_double(int x)
{
return x;
}
std::shared_ptr<int> i(new int(3));
std::shared_ptr<double> d = fmap(to_double, i);
std::vector<int> is = { 1, 2, 3 };
std::vector<double> ds = fmap(to_double, is);
There are two laws that functors should follow. The first is the identity law, which states that if the functor is given an identity function, it should be the same as applying the identity function to the type, that is fmap(identity, x)
should be the same as identity(x)
:
函子应该遵循两条定律。第一个是恒等律,它规定如果给函子一个恒等函数,它应该与将恒等函数应用于类型相同,即fmap(identity, x)
应该与 相同identity(x)
:
struct identity_f
{
template<class T>
T operator()(T x) const
{
return x;
}
};
identity_f identity = {};
std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<int> is1 = fmap(identity, is);
std::vector<int> is2 = identity(is);
The next law is the composition law, which states that if the functor is given a composition of two functions, it should be the same as applying the functor for the first function and then again for the second function. So, fmap(std::bind(f, std::bind(g, _1)), x)
should be the same as fmap(f, fmap(g, x))
:
下一个定律是复合定律,它指出如果给函子给出两个函数的组合,它应该与将函子应用于第一个函数然后再次应用于第二个函数相同。所以,fmap(std::bind(f, std::bind(g, _1)), x)
应该是一样的fmap(f, fmap(g, x))
:
double to_double(int x)
{
return x;
}
struct foo
{
double x;
};
foo to_foo(double x)
{
foo r;
r.x = x;
return r;
}
std::vector<int> is = { 1, 2, 3 };
// These two statements should be equivalent.
// is1 should equal is2
std::vector<foo> is1 = fmap(std::bind(to_foo, std::bind(to_double, _1)), is);
std::vector<foo> is2 = fmap(to_foo, fmap(to_double, is));
回答by Fellow Traveler
Here's an actual situation where I was forced to use a Functor to solve my problem:
这是我被迫使用 Functor 来解决我的问题的实际情况:
I have a set of functions (say, 20 of them), and they are all identical, except each calls a different specific function in 3 specific spots.
我有一组函数(比如其中的 20 个),它们都是相同的,除了每个函数在 3 个特定位置调用不同的特定函数。
This is incredible waste, and code duplication. Normally I would just pass in a function pointer, and just call that in the 3 spots. (So the code only needs to appear once, instead of twenty times.)
这是令人难以置信的浪费和代码重复。通常我只会传入一个函数指针,然后在 3 个位置调用它。(所以代码只需要出现一次,而不是二十次。)
But then I realized, in each case, the specific function required a completely different parameter profile! Sometimes 2 parameters, sometimes 5 parameters, etc.
但后来我意识到,在每种情况下,特定功能都需要完全不同的参数配置文件!有时是 2 个参数,有时是 5 个参数,等等。
Another solution would be to have a base class, where the specific function is an overridden method in a derived class. But do I really want to build all of this INHERITANCE, just so I can pass a function pointer????
另一种解决方案是拥有一个基类,其中特定函数是派生类中的重写方法。但是我真的想构建所有这些继承,只是为了传递一个函数指针????
SOLUTION: So what I did was, I made a wrapper class (a "Functor") which is able to call any of the functions I needed called. I set it up in advance (with its parameters, etc) and then I pass it in instead of a function pointer. Now the called code can trigger the Functor, without knowing what is happening on the inside. It can even call it multiple times (I needed it to call 3 times.)
解决方案:所以我所做的是,我创建了一个包装类(一个“函数”),它能够调用我需要调用的任何函数。我提前设置了它(使用它的参数等),然后我传递它而不是函数指针。现在被调用的代码可以触发 Functor,而无需知道内部发生了什么。它甚至可以多次调用它(我需要它调用 3 次。)
That's it -- a practical example where a Functor turned out to be the obvious and easy solution, which allowed me to reduce code duplication from 20 functions to 1.
就是这样——一个实际的例子,一个 Functor 被证明是显而易见的和简单的解决方案,它使我能够将代码重复从 20 个函数减少到 1 个。
回答by Yantao Xie
回答by nfries88
Like has been repeated, functors are classes that can be treated as functions (overload operator ()).
就像已经重复过的那样,函子是可以被视为函数的类(重载运算符 ())。
They are most useful for situations in which you need to associate some data with repeated or delayed calls to a function.
在需要将某些数据与对函数的重复或延迟调用相关联的情况下,它们最有用。
For example, a linked-list of functors could be used to implement a basic low-overhead synchronous coroutine system, a task dispatcher, or interruptable file parsing. Examples:
例如,函子的链表可用于实现基本的低开销同步协程系统、任务调度程序或可中断文件解析。例子:
/* prints "this is a very simple and poorly used task queue" */
class Functor
{
public:
std::string output;
Functor(const std::string& out): output(out){}
operator()() const
{
std::cout << output << " ";
}
};
int main(int argc, char **argv)
{
std::list<Functor> taskQueue;
taskQueue.push_back(Functor("this"));
taskQueue.push_back(Functor("is a"));
taskQueue.push_back(Functor("very simple"));
taskQueue.push_back(Functor("and poorly used"));
taskQueue.push_back(Functor("task queue"));
for(std::list<Functor>::iterator it = taskQueue.begin();
it != taskQueue.end(); ++it)
{
*it();
}
return 0;
}
/* prints the value stored in "i", then asks you if you want to increment it */
int i;
bool should_increment;
int doSomeWork()
{
std::cout << "i = " << i << std::endl;
std::cout << "increment? (enter the number 1 to increment, 0 otherwise" << std::endl;
std::cin >> should_increment;
return 2;
}
void doSensitiveWork()
{
++i;
should_increment = false;
}
class BaseCoroutine
{
public:
BaseCoroutine(int stat): status(stat), waiting(false){}
void operator()(){ status = perform(); }
int getStatus() const { return status; }
protected:
int status;
bool waiting;
virtual int perform() = 0;
bool await_status(BaseCoroutine& other, int stat, int change)
{
if(!waiting)
{
waiting = true;
}
if(other.getStatus() == stat)
{
status = change;
waiting = false;
}
return !waiting;
}
}
class MyCoroutine1: public BaseCoroutine
{
public:
MyCoroutine1(BaseCoroutine& other): BaseCoroutine(1), partner(other){}
protected:
BaseCoroutine& partner;
virtual int perform()
{
if(getStatus() == 1)
return doSomeWork();
if(getStatus() == 2)
{
if(await_status(partner, 1))
return 1;
else if(i == 100)
return 0;
else
return 2;
}
}
};
class MyCoroutine2: public BaseCoroutine
{
public:
MyCoroutine2(bool& work_signal): BaseCoroutine(1), ready(work_signal) {}
protected:
bool& work_signal;
virtual int perform()
{
if(i == 100)
return 0;
if(work_signal)
{
doSensitiveWork();
return 2;
}
return 1;
}
};
int main()
{
std::list<BaseCoroutine* > coroutineList;
MyCoroutine2 *incrementer = new MyCoroutine2(should_increment);
MyCoroutine1 *printer = new MyCoroutine1(incrementer);
while(coroutineList.size())
{
for(std::list<BaseCoroutine *>::iterator it = coroutineList.begin();
it != coroutineList.end(); ++it)
{
*it();
if(*it.getStatus() == 0)
{
coroutineList.erase(it);
}
}
}
delete printer;
delete incrementer;
return 0;
}
Of course, these examples aren't that useful in themselves. They only show how functors can be useful, the functors themselves are very basic and inflexible and this makes them less useful than, for example, what boost provides.
当然,这些例子本身并没有那么有用。它们只展示了函子如何有用,函子本身非常基本且不灵活,这使得它们不如 boost 提供的有用。