C++ 按引用传递和按值传递在功能上是否不同?

C++ 按引用传递和按值传递在功能上是否不同?,c++,pass-by-reference,pass-by-value,C++,Pass By Reference,Pass By Value,以下各项之间是否存在功能差异: void foo(const Bar& bar) { Bar bar_copy(bar); // Do stuff with bar_copy } 及 有一些区别 void foo(const Bar& bar) { Bar bar_copy(bar); // Do stuff with bar_copy } 不允许避免复制,即使bar是临时的,也不允许移动bar是的,这是有价值的区别 void foo(Bar)可以复制构造或移

以下各项之间是否存在功能差异:

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


有一些区别

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

不允许避免复制,即使
bar
是临时的,也不允许移动
bar

是的,这是有价值的区别

void foo(Bar)
可以复制构造或移动构造
Bar
,具体取决于调用上下文

当一个临时文件被传递到
foo(Bar)
时,您的编译器可以直接在
Bar
预期的位置构造该临时文件。给那个男孩一个帽尖

您的函数
void foo(const Bar&Bar)
始终执行复制


您的函数可以执行复制或移动,或者两者都不执行。

是的,存在差异。虽然最明显的一点是函数的类型发生了变化(因此函数指针的类型也发生了变化),但也有一些不太明显的含义:

移动可构造但不复制可构造的
Bar
例如,假设以下对
foo
的调用:

foo(Bar());
对于第一个版本,它将通过引用
const bar
传递,然后使用复制构造函数进行复制。对于第二个版本,编译器将首先尝试移动构造函数

这意味着,只有第二个版本可以被仅可移动构造的类型调用,如
std::unique_ptr

显然,这可以通过增加一个轻微的并发症来缓解:

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));
}
访问说明符 有趣的是,还有另一个区别:检查访问权限的上下文

考虑以下

class Bar
{
    Bar(Bar const&) = default;
    Bar(Bar&&) = default;

public:
    Bar() = default;

    friend int main();
};
int main()
{
    fooB(Bar()); // OK: Main is friend
}
现在,引用和复制版本将出错,而参数as value version将不会抱怨:

void fooA(const Bar& bar)
{
    //Bar bar_copy(bar); // error: 'constexpr Bar::Bar(const Bar&)' is private
}

void fooB(Bar bar) { } // OK
由于我们已将
main
声明为好友,因此下面的调用(请注意,如果实际调用是在
Bar
static
成员函数中进行的,则不需要该好友):

呼叫现场
条码的完整性
正如在评论中所观察到的,如果您希望调用站点的
Bar
类型不完整,可以使用通过引用版本,因为这不要求调用站点能够分配
Bar
类型的对象

复制省略副作用 C++11 12.8/31:

当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使该对象的复制/移动构造函数和/或析构函数有副作用。在这种情况下, 该实现将省略的复制/移动操作的源和目标视为两个不同的操作 引用同一对象的方式[…]

  • [……]
  • 复制/移动未绑定到引用(12.2)的临时类对象时 对于具有相同cv类型的类对象,可以通过 将临时对象直接构造到省略的复制/移动的目标中
  • [……]

显然,只有按值调用版本符合此标准-在按引用传递后,参数毕竟绑定到引用,这也意味着失去了一个优化机会。

我想你的意思是
Bar\u copy(Bar)?我一直用它来声明函数采用不完整的类型(通过引用),这些类型的定义后来包含在
.cpp
文件中。@LogicStuff您使用最上面的类型吗?为什么不能用最下面的那个呢?@wrhall,因为函数声明中的参数必须是完整类型的,如果它不是通过引用或指针传递的话。@wrhall-编译器需要知道如何构造(当然也要分配)
Bar
,因为它是在函数调用之前放在堆栈上的。调用者需要知道对象的大小。所以它必须被完全指定。引用和指针只作为指针(通常)传递,指针的大小是已知的(即
sizeof(void*)
),因此在进入函数之前,您不需要知道对象的大小。如果只传递值,这是否会使情况变得更糟[也就是说,有一条关于这个问题的评论说,他们使用pass-by-reference,然后对采用不完整类型的函数进行复制,而这些函数可能无法在pass-by-value版本中使用]?@wrhall:如果你按值传递,你允许移动构造
bar
bar
。对——所以在你可以按值传递的情况下,你可能应该,对吧?或者有理由不按注释中的状态,向前声明。为了“简单”,因为在其他更频繁的情况下,首选的方法是通过常量引用传递。您还错过了一个优化机会,因为当参数的初始值设定项是临时的时,可以完全忽略复制或移动。
int main()
{
    fooB(Bar()); // OK: Main is friend
}