之间是否存在功能差异:
void foo(const Bar& bar) {
Bar bar_copy(bar);
// Do stuff with bar_copy
}
和
void foo(Bar bar) {
// Do stuff with bar
}
答案 0 :(得分:22)
是的,这是有价值的差异。
void foo(Bar bar)
可以复制构造或 move-construct bar
,具体取决于调用上下文。
当临时传递给foo(Bar bar)
时,您的编译器可能能够在bar
预期的位置直接构造该临时。帽子提示模板男孩。
您的函数void foo(const Bar& bar)
始终执行副本。
您的功能void foo(Bar bar)
可能执行复制或移动,或者可能都不执行。
答案 1 :(得分:13)
是的,有差异。虽然最明显的一个是函数的类型发生了变化(因而也就是函数指针的类型),但也有一些不太明显的含义:
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();
};
现在,引用和复制版本将出错,而参数值为版本不会抱怨:
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(请注意,如果实际呼叫是在static
成员中进行,则不需要该朋友Bar
)的功能:
int main()
{
fooB(Bar()); // OK: Main is friend
}
Bar
的完整性正如评论中所观察到的那样,如果您希望Bar
在呼叫站点处是一个不完整的类型,则可以使用pass-by-reference版本,如这不需要调用站点能够分配类型为Bar
的对象。
C ++ 11 12.8 / 31:
当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使对象的复制/移动构造函数和/或析构函数具有副作用。在这种情况下, 该实现将省略的复制/移动操作的源和目标视为两个不同的 引用同一个对象的方式[...]
- [...]
- 当复制/移动尚未绑定到引用(12.2)的临时类对象时 对于具有相同cv-unqualified类型的类对象,可以省略复制/移动操作 将临时对象直接构造到省略的copy / move
的目标中- [...]
显然,只有按值调用的版本符合此标准 - 在通过引用传递之后,参数毕竟被绑定到引用。 Beyond an observable difference,这也意味着失去了优化机会。
答案 2 :(得分:4)
存在一些差异。
void foo(const Bar& bar) {
Bar bar_copy(bar);
// Do stuff with bar_copy
}
即使bar
是临时的,也不允许避免复制,也避免移动bar
。