右值引用可以作为 const 引用传递吗?

Can rvalue references be passed on as const references?

考虑以下示例 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++ 中明确定义的行为?

是的,允许。

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

此外,如果您使用 std::movea 创建右值,您的代码仍然是格式正确的,因为右值可以绑定到 const 左值引用:

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

您需要在您的示例中将 a 转换为右值以获得预期效果,因为变量名称本身是左值(这是 C++ 中左值和右值的棘手细节)。所以正如所写的那样,它是正确的 C++,但没有按照您的想法去做。

如果你施放它,使用std::move(a) instead of just a, the code now does what you want and is still correct. This is because of a special rule in C++ that temporaries can be bound to const lvalues, a more detailed discussion of which can be found here。当您使用如下代码时,此功能会非常方便:

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

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

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

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