是的,有区别。虽然最明显的是函数的类型发生了变化(因此函数指针的类型也发生了变化),但也有一些不太明显的含义:
可移动构造但不可复制构造Bar
例如,假设对foo 的以下调用:
foo(Bar());
对于第一个版本,这将通过对const bar 的引用传递,然后使用复制构造函数进行复制。对于第二个版本,编译器将首先尝试移动构造函数。
这意味着,只有第二个版本可以由只能移动构造的类型调用,例如std::unique_ptr。 In fact, the manually forced copy will not even allow compilation of the function.
显然,这可以通过增加一点复杂性来缓解:
void foo(Bar&& bar) {
// Do something with bar.
// As it is an rvalue-reference, you need not copy it.
}
void foo(Bar const& bar) {
Bar bar_copy(bar);
foo(std::move(bar_copy));
}
访问说明符
有趣的是,还有另一个区别:检查访问权限的上下文。
考虑以下Bar:
class Bar
{
Bar(Bar const&) = default;
Bar(Bar&&) = default;
public:
Bar() = default;
friend int main();
};
现在,reference-and-copy 版本会报错,而 parameter-as-value 版本不会报错:
void fooA(const Bar& bar)
{
//Bar bar_copy(bar); // error: 'constexpr Bar::Bar(const Bar&)' is private
}
void fooB(Bar bar) { } // OK
由于我们已将main 声明为朋友,因此以下调用is allowed(请注意,如果实际调用是在Bar 的static 成员函数中进行的,则不需要朋友) :
int main()
{
fooB(Bar()); // OK: Main is friend
}
呼叫现场Bar 的完整性
正如在 cmets 中所观察到的,如果您希望 Bar 在调用站点上是一个不完整的类型,可以使用传递引用版本,就像这样不需要调用站点能够分配Bar 类型的对象。
复制省略的副作用
C++11 12.8/31:
当满足某些条件时,允许实现省略类的复制/移动构造
对象,即使对象的复制/移动构造函数和/或析构函数有副作用。在这种情况下,
该实现将省略的复制/移动操作的源和目标视为简单的两个不同
引用同一对象的方式 [...]
- [...]
- 何时复制/移动尚未绑定到引用 (12.2) 的临时类对象
对于具有相同 cv-unqualified 类型的类对象,可以通过以下方式省略复制/移动操作
将临时对象直接构造到省略的复制/移动的目标中
- [...]
显然,只有按值调用的版本符合这个标准——在通过引用传递之后,参数毕竟绑定到一个引用。 Beyond an observable difference,这也意味着失去了优化机会。