【问题标题】:Why would you ever take the copy of an object as a parameter to your function? Why are not const ref the default way of parameters?为什么要将对象的副本作为函数的参数?为什么 const ref 不是默认的参数方式?
【发布时间】:2020-06-16 17:54:21
【问题描述】:

尽管我很喜欢 C++ 编程,但有一件事我真的不明白。在我看来,最常见的函数编程方式是这样的:

some_function(a variable)
    do something according to the data in the variable

示例:

bool match_name(const std::string& name)
{
    return this->name == name;
}

我发现自己在代码中对 90% 的函数参数使用了 const ref(也许我做错了什么)。

我的问题是:为什么变量的副本是“默认”类型的参数?为什么 const ref 不是默认值?

为什么不这样呢?:

void my_function(My_type object)      // <- const ref

void my_function(ref My_type object)  // <- ref (mutable)

void my_function(copy My_type object) // <- copy

【问题讨论】:

  • void my_function(My_type object) // &lt;- const ref 会让 C 代码在 C++ 中运行非常痛苦。
  • 这里的一件大事是,在最初创建该语言时甚至不存在引用。在大多数情况下,更改基本语法是一个大问题。更不用说向后兼容性了。
  • 因为 C++ 是从 C 开始的,带有类
  • 我猜,从历史上看(以及在 C 时代),许多(如果不是大多数)函数并不期望修改它们的参数,而是返回一个计算值。例如。标准函数,如 sqrt(x)sin(x) 和(如果实现为函数而不是宏)max(a,b)
  • C++ 有很多错误的默认值,Rust 默认使用 move,有 mut(able) 而不是 const,...

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


【解决方案1】:

除了历史原因,这将产生非常丑陋的影响。这基本上意味着MyType myVarName在函数范围或函数参数列表中使用时会有不同的含义。

void foo(MyType myVar) //myVar is const ref
{
    MyType anotherVar = myVar; // anotherVar is a copy, what?
    const MyType& myVarRef = myVar; //a reference, but it looks like it has different type than myVar?
}

想想可怜的&amp;!除非你提出一种不同的方式来区分 refs 和 non-refs,否则你最终会得到

void foo(MyType& myVar) //myVar is a copy?
{
    MyType& anotherVar = myVar; // anotherVar is a reference???
}

C++ 有一个完善的对象和对象引用的概念。后者总是通过在类型中添加&amp; 来表示。你的提议会搞砸,C++ 会比它更难理解。

【讨论】:

  • 如果 C++ 改为使用“ref”和“copy”关键字,MyType anotherVar = myVar; 也会使 anotherVar 成为 const ref。要复制,可能是copy MyType anotherVar = myVar; 或类似的东西...
【解决方案2】:

C++ 想要与 C 兼容,由于 C 默认使用值语义,所以 C++ 也是如此。如果按值采用内置类型,而按引用采用其他类型,则会非常不一致。

但是,通过引用传递并不总是更好。引用导致的指针间接通常会使编译器更难优化代码生成。在许多情况下,按值传递类型仍然比通过 ref 更好。这取决于类型。引用的指针间接可能会导致内存读取,而按值传递允许对象在 CPU 寄存器中传递。比如这样的类型:

class Point {
public:
    /* functions */
private:
    int x;
    int y;
};

应始终按值传递。它只是两个整数。引用可能会导致不必要的内存读取以获取值。

此外,有时即使类型无法注册,您也希望按值传递。例如,由于移动语义,需要存储传递的值的所谓“接收器函数”对值的性能更好。主要的例子是constructros和setter:

void SomeClass::setString(std::string s)
{
    this->s_ = std::move(s);
}

在这种情况下通过引用传递可能会产生额外的副本。您可以在线阅读更多信息。一个很好的起点是这个 SO 问题:

Are the days of passing const std::string & as a parameter over?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2010-11-23
    • 1970-01-01
    • 2011-02-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-05-31
    相关资源
    最近更新 更多