所以以下所有内容都是基于意见的,但这些是我在考虑 API 时倾向于遵循的规则。与往常一样,在 C++ 中,有很多方法可以完成相同的事情,而且人们对于什么是最好的会有不同的看法。
我们需要考虑三种参数:in 参数、out 参数和in/out 参数。后两者很简单,所以我们先介绍它们。
输出参数
不要使用它们。严重地。如果您的函数要返回一个新对象,则按值返回它。如果您要返回多个 新对象,则按包装在std::tuple(或std::pair)中的值返回它们。调用者可以使用 std::tie(或 C++17 中的结构化绑定)再次解压它们。这为调用者提供了最大的灵活性,并且使用 RVO,它的效率不亚于任何其他方法。
输入/输出参数
对于修改已构造值的函数,请使用可变左值引用,即T&。这将阻止调用者传递一个临时的,但这实际上是一件好事:修改你将要丢弃的东西有什么意义?并不是说某些样式指南(尤其是 Google 的,还有 Qt 的)提倡在这种情况下使用原始指针 (T*),因此在调用站点上很明显该参数将被修改(因为您需要说 f(&arg) ),但我个人认为这并不令人信服。
在参数中
对于纯输入参数,函数不会修改传递给它的参数,事情会稍微复杂一些。一般来说,最好的建议是通过lvalue-reference-to-const,即const T&。这将允许调用者同时传递左值和右值。但是,对于小对象(sizeof(T) <= sizeof(void*)),例如int,改为按值传递会更高效。
一个例外是,如果您要获取传递参数的副本,例如在构造函数中;在这种情况下,最好按值取参数,因为编译器可以把它变成右值的移动。
T&& 呢?
有两种情况适合使用T&& 形式的参数。第一个是模板化的转发函数,参数的类型是模板类型,即
template <typename T>
decltype(auto) func(T&& arg) {
return other_func(std::forward<T>(arg));
}
在这种情况下,虽然参数看起来像是一个右值引用,但它实际上是一个转发引用(有时称为通用引用)。仅使用转发引用通过std::forward 将内容传递给另一个函数;如果你关心参数的值类别,那么T&& 是不合适的。
第二种情况是真正的右值引用,其中实参类型不是模板形参。在极少数情况下,可能适合在const arg& 和arg&& 表单上重载,以避免不必要的移动。这应该只在您要在某处复制或移动参数的性能关键情况下是必需的(例如,std::vector 为其push_back() 方法执行此操作)——通常我会说最好采取按值传递参数,然后将其移动到位。