在某些情况下,std::function
可以替换继承。以下两个代码片段非常相似(调用函数时的成本相同,签名中的用法几乎相同,在大多数情况下,std :: function不需要我们另外复制A
) :
struct Function
{
virtual int operator()( int ) const =0;
};
struct A
: public Function
{
int operator()( int x ) const override { return x; }
};
使用std::function
,我们可以将其重写为
using Function = std::function<int (int)>;
struct A
{
int operator()( int x ) const { return x; }
};
为了更清楚,两个片段是如何相关的:它们都可以通过以下方式使用:
int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }
int main( int argc, char **argv )
{
A f;
anotherFunction( f, 5 );
return 0;
}
后一种方法更灵活,因为我们不必从一些公共基类派生我们的类。类似Function
的对象之间的唯一关系是基于它们的能力。在面向对象的编程方面,它可能被认为不太干净(当然不是在函数式编程方面)。
除此之外,两种解决方案之间还有其他差异吗?是否有任何一般性指导原则何时使用哪种解决方案或只是个人偏好?是否存在一种解决方案胜过另一种解决方案的情况?
答案 0 :(得分:20)
首先进行小修正:注意,这个:
int anotherFunction( Function f, int x ) { return f( x ) + f( x ); }
不会使用基于继承的解决方案进行编译,因为Function
是按值获取的,而且它是抽象的。如果它不是抽象的,另一方面,你会得到切片,这不是你想要的。
相反,您必须通过引用(可能通过引用Function
)来获取const
派生的对象,以便利用多态性:
int anotherFunction( Function const& f, int x ) { return f( x ) + f( x ); }
这不是很像功能,所以如果你热衷于函数式编程(就像你似乎那样),你可能只想因此而避免它。
那就是说,这是我提供的指导原则:
如果可以,请使用模板:
template<typename F>
int anotherFunction(F f, int x) { return f(x) + f(x); }
通常,当它可以被使用时,静态(编译时,基于模板)多态被认为优于动态(运行时,基于继承)多态,因为:
卓越的灵活性:您不必更改类型的定义,并使它们从公共基类派生,以便一般使用它们。例如,这允许您编写:
anotherFunction([] (int x) { return x * 2; }, 42);
以及:
anotherFunction(myFxn, 42); // myFxn is a regular function
甚至:
anotherFunction(my_functor(), 42); // my_functor is a class
卓越的性能:由于您没有通过虚拟表进行调用,并且编译器知道如何解析函数调用,因此它可以内联调用以提供更高的性能(如果它认为这是合理的)。
如果您无法使用模板,因为要调用的函数将在运行时确定,使用std::function
:< / p>
int anotherFunction(std::function<int (int)> f, int x)
{
return f(x) + f(x);
}
这也将为您提供足够的灵活性来传递lambdas,函数指针,仿函数,基本上任何可调用对象。例如,请参阅this Q&A on StackOverflow。
使用std::function
可能会对基于模板的设计带来显着的运行时开销,并且对于基于硬编码继承的解决方案(如您概述的解决方案)而言可能也会带来轻微的小开销,但它为您提供灵活性和这是一个标准的习惯用语。此外,一如既往地考虑性能,请进行测量以备份任何假设 - 您可能会得到令人惊讶的结果。
当您希望存储任何类型的可调用对象以供以后调用时,您通常需要采用这种基于std::function
的设计,例如{{3或者,当你有一个异类的可调用对象集合要进行处理和一般调用时。有关何时使用std::function
而不是模板的讨论,请参阅Command design pattern。
那么什么时候应该采用使用继承的硬编码设计?好吧,在所有那些1.和2.不可行的情况下 - 老实说,我现在想不出任何问题,但我确信有人可以提出一个角落案例。请注意,由于Boost具有std::function
和boost::function
实现,因此C ++ 11 不是必要的,以便使用boost::bind
这样的习惯用法。 C ++ 11 std::function
和std::bind
之前的日期(并作为其灵感)。
TL; DR :使用模板或std::function
。