许多标准库算法取谓词的功能。 然而,这些谓词的类型是任意的,用户提供的模板的参数。 为什么不C ++ 11规定,这些都需要一个特定的类型,比如std::function
呢? 例如:
template< class InputIt >
InputIt find_if( InputIt first, InputIt last,
std::function<bool()> p );
是不是用这个代替模板作为参数类型不是更干净?
许多标准库算法取谓词的功能。 然而,这些谓词的类型是任意的,用户提供的模板的参数。 为什么不C ++ 11规定,这些都需要一个特定的类型,比如std::function
呢? 例如:
template< class InputIt >
InputIt find_if( InputIt first, InputIt last,
std::function<bool()> p );
是不是用这个代替模板作为参数类型不是更干净?
std::function
是运行时多态性。 有什么特别std::function
实例可以存储任何类型(A型这是适合的函子std::function
的签名,当然)。
标准库算法和如模板它们的功能参数的类型。 因此,他们并不需要运行时多态性做好自己的工作; 他们依靠编译时多态性。
最重要的是,这样的算法并不需要强制你运行时多态性的成本 。 如果你想运行时多态性,你可以给它std::function
或什么的。 如果你想编译时多态,你提供给它一个类型不使用多态分派(又名:大部分仿函数或函数)。
运行时多态性的成本还包括无法内联函数调用。 使用合适的仿函数(或偶函数指针,这取决于你的编译器有多好),编译器通常可以内联函数调用,如果它希望这样。 随着运行时多态性,不仅是你付出的运行调度的成本(可能包括额外的参数转发成本),你也失去重要的优化机会。
性能!
模板库功能是非常非常不是更好std::function
模式。 我做了这个测试给你:
template <typename F>
void test1(const F &f)
{
for (unsigned long long i = 0; i < 19000000; i++)
f();
}
void test2(function<void()> &f)
{
for (unsigned long long i = 0; i < 19000000; i++)
f();
}
int main()
{
{
LARGE_INTEGER frequency, start, end;
double interval;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
unsigned long long x = 0;
test1([&x]()
{
x++;
});
QueryPerformanceCounter(&end);
interval = (double) (end.QuadPart - start.QuadPart) / frequency.QuadPart;
cout << "Template mode: " << interval << " " << x << endl;
}
{
LARGE_INTEGER frequency, start, end;
double interval;
QueryPerformanceFrequency(&frequency);
QueryPerformanceCounter(&start);
unsigned long long x = 0;
function<void() > f = [&x]()
{
x++;
};
test2(f);
QueryPerformanceCounter(&end);
interval = (double) (end.QuadPart - start.QuadPart) / frequency.QuadPart;
cout << "std::function mode:" << interval << " " << x << endl;
}
}
模板模式:2.13861e-006
的std ::功能模式:0.220006
Windows7的上GCC 4.7.2 -O2
Core2双核CPU主频为2.40GHz
因为std::function
不完善。
它是如何完美? 让我列举的方式。
{
首先, std::function
不支持将传入任意对象的完美fowarding。 而且,在实践中,它不能。 std::function
公开了一个固定的签名来电,并能接受多种被叫方的,完善的转发需要每个主叫方和被叫者量身定做的签名。 它支持的正是在其签名的公开争论完美的转发,但这是不够的。
想象一下,一个std::function
有两个参数,并int
和double
。 为它做完美转发,就必须接受int&
, int&&
和int const&
,同组次double
(更不用说挥发性一些变体物)。 每个签名数量std::function
必须接受决绝完美转发与它有参数的数量呈指数级增长。 std::function
的一套它暴露(目前1)被固定在实例化,而将其设置暴露签名的模板是无限的,仅用于当生成的签名的。 因为有些功能状物体做优化这些情况有所不同这一点很重要! 所以每次std::function
已取出的机会,完全向前调用包装类型。
第二个原因std::function
不完善是因为编译器吸。 如果包装在一个lambda std::function
然后调用它的算法,在理论上,编译器可以实现这个std::function
是包装一些固定的λ -但在实践中,它失去了这一事实的轨道,和对待该std::function
为周围的一些通用类的包装。 因此,即使在签名案件std::function
完全匹配算法的使用情况,防止类型瓶颈std::function
作出转发不完善,在实践中会有开销由于类型-擦除所进行std::function
,编译器会觉得很难优化过std::function
调用“屏障”。
第三个原因std::function
不完善的是,它会鼓励算法作家过分限制哪些参数可以在算法进行传递。 如果您检查find_if
,天真的假设是,你正在寻找的东西应该是相同类型储存在容器中的类型,或者至少可转换:但std::find_if
算法只要求它们在传递comparible在仿函数。
这允许你写多类型的感知函子,并通过在目标对象,它是一个不相关类型的容器的类型,以及工作的事情就好了。 大多数人并不需要这一点,他们的代码的作品没有找到它 - 这是很好的为好。
幼稚std::find_if
将提取基础类型的容器的,并比较功能将是对这种类型的间-或,这将是被搜索的容器类型和事物的类型之间的4路比较。 在一个案例中,我们失去弹性 - 在另一种情况下,每个人都付出了一个奇怪的极端情况。 而在C ++中,你应该只支付当你需要他们,你需要的功能!
A中提出理由std::function
不完善的是,它基本上是类型擦除工具。 这些都是实现细节,但我不知道该从他们迷路远编译器的。 一个std::function
的目的是揭露单一的签名和返回值,并说:‘我可以存储这个签名,这个返回值匹配任何东西,你可以把它称为’。 它公开了一个静态的运行时间接口和实现做这个任务。 当你初始化std::function
,它编译时工作,产生包装辅助对象,在统一的特定对象std::function
接口,然后存储,在pImpl
模式。 所有这些是不必要的,如果你不需要类型擦除工作。
标准算法是如何编写高水平的代码,几乎与手工制作的解决方案,高效。 即使不需要指针函数调用的成本来解决这些问题的大部分,沿虚拟呼叫让通过清除类型std::function
。
}; // enum
std::function
是回调一个真棒工具,以取代样板单一用途virtual
接口,当你需要隐藏主叫方的实现细节(比如,你需要跨越的设计决策编译单元边界)。
好消息是,这个问题更好的解决方案被灌进管道。 特别是,对C ++ 14或C ++ 17的目标之一是,它要具有某种“概念”的支持,在这里你可以说“这个模板参数具有以下特性”。 什么确切的语法会远一些 - 在C ++ 11概念提案很可能将路远 - 但有很多关于它的积极性,并有现在对这个问题的工作组。
当或者如果它这样做,你就可以庆祝了有意义的概念信息的函子,说:“这种说法是不是任何类型,而是一种类型,它是取两个值(包括所包含的数据类型算符),并返回一个bool
兼容值”的编译器,你的IDE,你可以不必再为函数的文档了解。