是通过引用传递然后复制和传递值在功能上不同?

时间:2015-04-02 20:57:17

标签: c++ pass-by-reference pass-by-value

之间是否存在功能差异:

void foo(const Bar& bar) {
  Bar bar_copy(bar);
  // Do stuff with bar_copy
}

void foo(Bar bar) {
  // Do stuff with bar
}

3 个答案:

答案 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_ptrIn 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