当一个函数引用一个右值引用时,该函数中该变量的类型是什么?

时间:2019-06-14 17:07:31

标签: c++ rvalue-reference lvalue-to-rvalue

这是术语问题。如果我有这个:

#include <vector>

void g(std::vector<int>&& arg);

void f0(std::vector<int>&& v) {
    static_assert(std::is_same<decltype(v), std::vector<int>&&>::value); // Looks like v is an rvalue reference.
    static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
    return g(std::move(v)); // Fine.
}

那么v是什么类型?如果您在谈论调用f0,您会说“ f0具有右值引用”(对吗?),但是在f0中,v不是右值引用,还是不需要std::move?对?但是static_assert表明它是右值,对吧?

类似地:

void f1(std::vector<int>&& v) {
    static_assert(std::is_same<decltype(v), std::vector<int>&&>::value);
    static_assert(std::is_same<decltype((v)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(v)>::type, std::vector<int>>::value);
    return g(v); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'.
    // So is v just a std::vector<int>?
}

本地右值引用的行为相同:

void f2(std::vector<int>&& v) {
    std::vector<int>&& vv = std::move(v);
    static_assert(std::is_same<decltype(vv), decltype(v)>::value, "They are the same decltype. So being an argument isn't magic.");
    static_assert(std::is_same<decltype(vv), std::vector<int>&&>::value);
    static_assert(std::is_same<decltype((vv)), std::vector<int>&>::value);
    static_assert(std::is_same<std::decay<decltype(vv)>::type, std::vector<int>>::value);
    return g(vv); // Error: cannot bind rvalue reference of type 'std::vector<int>&&' to lvalue of type 'std::vector<int>'
}

描述v类型的正确术语是什么?说f0是否使用右值引用是否正确?如果v是右值引用,那么用什么术语说不能使用右值引用来调用带有右值引用的函数?

2 个答案:

答案 0 :(得分:3)

名为v的变量的声明类型std::vector<int>&&。将该类型读取为“ std::vector的右值引用”。

名称v可以出现在表达式中。表达式永远不会具有引用类型[expr.type]/1。但是表达式有一个value category。当名称v出现在v[0]中的表达式中时,子表达式v的类型为std::vector<int>,其值类别为 lvalue 。几乎所有id-expression(表达式只是名称)都是这种情况。

decltype(v)给出变量v的声明类型。

decltype(expression)给出:

  • 如果expression是左值,则对expression类型的左值引用
  • 如果expression是xvalue,则对expression类型的右值引用
  • 如果expression是prvalue,则expression的类型。

[dcl.dcl]/1中提供了更多详细信息。

答案 1 :(得分:2)

您正在将类型与值类别混淆,这在您的辩护中非常容易做到。

是的,该函数接受类型为“ std::vector<int>的右值引用”的参数。可以从呼叫现场的std::vector<int>类型的右值表达式初始化此引用。

开始尝试使用函数内部的表达式v的类型不是std::vector<int>&&;参考类型的“衰减”。这只是引用工作原理的一部分。 (decltype在这方面有些奇怪。)出于所有意图和目的,您最终得到类型为std::vector<int> lvalue 表达式。无论如何,在这一点上类型是无关紧要的。关键是名称v是一个左值,而再次使其变为右值,则需要std::move

  

但是static_assert显示它是一个右值,对吧?

不。 “右值参考”描述了各种类型。 “ rvalue”是值类别。您可能想知道为什么他们选择了这种令人困惑的术语。我也是。