【问题标题】:Can rvalue references be passed on as const references?右值引用可以作为 const 引用传递吗?
【发布时间】:2019-10-08 19:38:45
【问题描述】:

考虑以下示例struct

struct A {
    int i;

    void init(const A &a)
    {
        this->i = a.i;
    }

    A(int i)
    {
        this->i = i;
    }

    A(const A &a) = delete;
    A &operator=(const A &a) = delete;

    A(A &&a)
    {
        init(a); // Is this allowed?
    }

    A &operator=(A &&a)
    {
        init(a); // Is this allowed?
        return *this;
    }
};

右值引用A &&a 被传递给接受const A &a 的函数,即常量左值引用。这是否允许并导致 C++ 中定义明确的行为?

【问题讨论】:

  • 类型为“右值引用”的变量的名称是左值,可以像其他所有左值一样使用。
  • @cpplearner 那么这是否意味着这是有效的?一秒钟前我在这里看到一条评论说“不”。
  • @HerpDerpington 我不确定您所说的“旨在按照它的方式工作”是什么意思。正如重复项中所解释的那样,您的代码在语法上和语义上是正确的,我认为很明显绑定引用的行为与所有其他引用的行为相同,因为重复项的答案没有提及其他任何内容。

标签: c++ move-semantics rvalue-reference


【解决方案1】:

是的,这是允许的。

注意,表达式a 的值类别是左值,即使a 的声明类型是右值引用。

此外,如果您从 astd::move 创建一个右值,您的代码仍然是格式良好的,因为右值可以绑定到 const 左值引用:

init(std::move(a)); // std::move(a) is an rvalue (to be precise, xvalue), but still OK

【讨论】:

  • 究竟是哪个a? :D 你的意思是A(A &&a)A &operator=(A &&a) 的参数吗?
  • @HerpDerpington:是的。
【解决方案2】:

您需要在示例中将a 转换为右值以获得预期效果,因为变量名本身就是左值(这是 C++ 中左值和右值的一个棘手细节)。所以正如所写,它是正确的 C++,但没有做你认为它正在做的事情。

如果你转换它,使用std::move(a) 而不仅仅是a,代码现在可以做你想要的并且仍然是正确的。这是因为 C++ 中有一条特殊规则,即临时变量可以绑定到 const lvalues,更详细的讨论可以在 here 找到。当您有以下代码时,此功能会非常方便:

void ProcessData(const std::vector<int>& input_vector);

然后您想使用以下内容对其进行测试:

ProcessData(std::vector<int>{1, 2, 3, 4, 5});

这使您不必在将对象作为 const 左值引用传递之前显式创建对象。注意这里的 const 很关键,没有它代码是不正确的。对此选择的理由有更详细的讨论here

【讨论】:

  • 这里使用std::move(a) 代替a 有什么变化?两者仍将调用init(const A &amp;a),因为init(A &amp;&amp;a) 没有过载。
  • @HerpDerpington 是的(当然假设绑定到初始引用的对象在其生命周期内,但无论如何该要求始终适用)。
  • 好问题@uneven_mark,我对此有点呆滞。在此处使用std::move 会导致您实际上将右值传递给函数init,而没有std::move 您传递的是左值。所以重要的一点是,如果没有它,代码就不会做 OP 所要求的事情,即将一个右值传递给一个预期为 const 左值引用的参数。同时,在这两种情况下,代码都可以工作并做同样的事情,事实上,如果某些编译器为两者生成相同的代码,我不会感到惊讶(除非我错过了一个棘手的微妙之处)。
  • “但不做你认为它正在做的事情” 听起来好像结果程序的行为会有所不同。将作为参数的表达式视为左值而不是右值,因为 “不做你认为它正在做的事情” 对我来说听起来有点牵强(潜在的重载问题在这里不适用) )。事实上,我没有看到 OP 在任何地方声称他们正在传递一个右值,只是他们正在传递一个右值引用,这是正确的。
  • 嗯,这是那些棘手的细节之一(正如我在回答中提到的)。 Here 是一个很好的讨论。
猜你喜欢
  • 2014-09-09
  • 1970-01-01
  • 2020-01-23
  • 1970-01-01
  • 1970-01-01
  • 2020-03-08
  • 1970-01-01
  • 2018-07-24
  • 1970-01-01
相关资源
最近更新 更多