我使用引用。这使得实现者可以进行抽象和选择,而不会过度负担客户(有些情况很重要,有些情况则不是)。
我还使用它们来保持一致的风格——我不喜欢看到公共接口通过细节来传递其实现。
瞬态和副本可能很昂贵——这取决于您正在传递的类型。按值返回表示该类型应该是可平凡构造、可交换、可复制、可移动的。编译器在这个领域可以做出一些很好的优化(RVO/移动),但您也可以做出明智的决策,以最小化实现中的昂贵操作。一旦您不再使用每个人都知道复制特性的类型,那么选择如何返回就变得非常复杂,因此我只保持简单并支持引用。
传递引用还有一些其他好处,例如当客户端希望使用传递的类型的子类时。
如果需要优化的程序:我经常删除复制构造函数和operator=,如果它们不是平凡的或可能的。通过可变引用传递允许您使用不可复制/可分配的类型。
在这个问题中使用的
std::string
的严格范围内:按值返回
std::string
是非常普遍的,而且已经专门为这种情况进行了许多优化——RVO、COW和移动是其中一些显著的优化。正如Voo在下面的评论中提到的那样,按值返回通常更容易阅读。对于
std::string
和更高级别的程序,按值返回不太可能成为一个问题,但是重要的是要测量,以便了解您正在使用的标准库实现所涉及的成本(您的问题表明性能可能很重要)。
一个重要的考虑因素是,如果您想改进现有程序,请确保您了解实现的执行方式,并学习如何在性能重要时最有效地使用类型。实现可能是针对实际使用编写和优化的,这意味着它们可能在某些情况下持悲观态度并猜测您,在某些情况下,您尝试提高性能的尝试可能已经实现或使用类型的非传统用法可能会降低性能。典型的
std::vector
的调整大小行为就是一个明显的例子。走高性能道路确实需要很多时间和复杂性,关于您需要了解以获得最佳结果的内容,这显然因您使用的实现和您使用的类型而异。如果性能很重要且值得投入非平凡的时间,那么学习您使用的类型的操作是一项值得进行的努力,可以带来显著的收益。
我还应该补充一点,我经常在低层级别工作——性能至关重要和/或资源有限。可能会有许多限制,包括没有异常、没有锁(也意味着没有堆分配)、最小的抽象成本,甚至受到动态多态性的限制。即使对于C++来说,这可以被认为是一个相当苛刻的领域。我选择引用核心低级部件,但如果我知道程序仅在更高级别的领域或单元测试中使用,我将放宽这个规则。
{}
按钮来格式化代码。 - Mat