在维基百科关于函数对象的文章中提到,当与for_each一起使用时,这些对象具有性能优势,因为编译器可以将它们“内联”。
在这个上下文中,我不太清楚这意味着什么...或者任何上下文,我很尴尬地说。感谢您的帮助!
for_each
模板的最后一个参数是一个函数对象。函数对象是可以使用()
运算符“调用”的东西(可能带有参数)。根据定义,有两种不同类型的函数对象:
()
运算符的类类型对象(所谓的函数对象)也是函数对象。现在,如果你想将一个普通函数用作for_each
的函数对象,它看起来会像下面这样:
inline void do_something(int &i) { /* do something */ }
int main() {
int array[10];
std::for_each(array, array + 10, &do_something);
}
在这种情况下,for_each
模板使用[推导]参数<int *, void (*)(int &)>
进行实例化。请注意,在此情况下,实际的函数对象值是作为函数参数传递的函数指针&do_something
。从for_each
函数的角度看,这是一个运行时值。由于它是一个运行时值,所以对函数对象的调用不能内联(就像一般情况下不可能通过函数指针内联任何调用一样)。struct do_something {
void operator()(int &i) { /* do something */ }
};
int main() {
int array[10];
std::for_each(array, array + 10, do_something());
}
在这种情况下,for_each
模板被实例化为具有[推导]参数<int *, do_something>
。从 for_each
中对函数对象的调用将被定向到 do_something::operator()
。目标调用在编译时已知且不变。由于目标函数在编译时已知,因此可以轻松地进行内联。for_each
。它是我们调用 for_each
时创建的 do_something
类的 [可能是“虚拟的”临时] 实例。但是,此运行时值对调用的目标没有影响(除非 operator ()
是虚拟的),因此不会影响内联。内联是编译器可以用函数本身的内容替换对函数的调用的过程。
这需要编译器在编译时知道函数的内容。
如果传递了函数指针,编译器通常无法执行此操作。
内联就是直接用函数体替换对该函数的每个调用。
这是针对小函数的优化,因为它减少了跳转到新函数然后返回的开销。
::std::for_each
的完整定义。对于聪明的编译器来说,它应该能够看到它被调用时带有一个特定的参数,这个参数恰好是一个声明为内联的函数的地址。编译器应该能够像类方法一样将函数调用内联。 - Omnifariousvoid(int&)
签名的不同函数调用for_each
,并且编译器被强制使用相同的、唯一的for_each<int*,void(*)(int&)>
实例化。也就是说,编译器必须每次都使用相同的for_each
实例化,并对do_something
函数执行间接调用。当然,聪明的编译器可以找到一种方式来悄悄地创建几个for_each
实例,正如您所说,但在一般情况下,这将是一个相当可疑的事情。 - AnT stands with Russiavoid foo(int)
并多次调用它如foo(1); foo(2); foo(3);
,我通常期望编译器生成一个foo
函数体并使用不同的参数 1、2 和 3 执行相同的函数体。如果我发现编译器生成了三个不同的foo
函数体,并分别使用“内联”常量 1、2 和 3,那么我会感到不愉快。对于小的foo
来说这没问题(如果之后被内联了),但对于较大的foo
来说则是不可取的。你所描述的本质上是相同的事情。 - AnT stands with Russiastd::for_each
的情况下,函数指针本身是函数参数(不是模板参数)。模板参数是函数指针类型。 - AnT stands with Russia在我的示例中,您可以使用具有 void (int&) 签名的不同函数调用 for_each,编译器被强制使用唯一一个 for_each 实例
,这与 ODR 有关吗? - user3882729