21

刚刚阅读了经典书籍《Effective C++, 3rd Edition》,在第20条中作者总结出内置类型、STL迭代器和函数对象类型更适合pass-by-value。我可以很好地理解内置和迭代器类型的原因,但是为什么函数对象应该是 pass-by-value,因为我们知道它无论如何都是类类型?

4

3 回答 3

19

在典型情况下,函数对象将很少或(更经常)没有持久状态。在这种情况下,按值传递可能根本不需要实际传递任何东西——传递的“值”基本上只是“这是对象”的占位符。

鉴于许多函数对象中的少量代码,这导致了进一步的优化:编译器通常很容易内联扩展函数对象的代码,因此不传递任何参数,并且根本不涉及函数调用。

当你传递一个指针或引用时,编译器可能也能做同样的事情,但这并不那么容易——更常见的是你最终会创建一个对象,传递它的地址,然后是函数通过该指针调用该对象的调用运算符。

编辑:可能还值得一提的是,这同样适用于 lambda,因为它们实际上只是伪装的函数对象。您不知道类的名称,但它们在紧邻的范围内创建了一个类,该类重载了函数调用运算符,这是在您“调用” lambda 时调用的。[谢谢@马克加西亚。]

于 2013-07-13T03:31:50.087 回答
5

按值传递函数对象的 #1 原因是因为标准库要求传递给其算法的函数对象是可复制的。C++11 §25.1/10:

[注:除非另有说明,否则将函数对象作为参数的算法可以自由复制这些函数对象。对象标识对他们来说很重要的程序员应该考虑使用指向非复制实现对象的包装类,例如reference_wrapper<T>(20.8.3),或一些等效的解决方案。——尾注]

其他答案很好地解释了理由。

于 2013-07-13T05:51:38.077 回答
2

来自 Effective STL(因为您似乎喜欢 Scott Meyers)第 38 条Design functor classes for pass-by-value

“在 C 和 C++ 中,函数指针都是按值传递的。STL 函数对象是在函数指针之后建模的,因此 STL 中的约定是函数对象在传递给函数和从函数传递时也是按值传递的。”

这有一些好处和一些含义,就像@Jerry Coffin 所说,编译器可以进行一些优化,比如内联代码以避免函数调用(你必须将你的仿函数标记为内联)。这种情况的一个很好的例子是 qsort 与 std::sort 性能比较,其中使用内联函子的 std::sort 比 qsort 性能要好很多,您可以在 Effective STL 上找到更多关于此的信息,其中广泛讨论并提到了几个章节。

这也有几个含义,因为函数对象是按值传递和返回的,你必须确保你的对象有一个定义良好的复制机制,尺寸小(否则它可能会变得昂贵),并且是单态的(因为传递多态按值的对象可能会导致对象切片)。

于 2013-07-13T04:01:56.993 回答