Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/154.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 为什么右值引用变量不是右值?_C++_Rvalue Reference_Rvalue - Fatal编程技术网

C++ 为什么右值引用变量不是右值?

C++ 为什么右值引用变量不是右值?,c++,rvalue-reference,rvalue,C++,Rvalue Reference,Rvalue,假设一个函数有两个重载ff(T&&)和f(T&)。 然后在g的正文中: g(T&&T){f(T);}将调用重载f(T&),因为T被视为左值 这让我很惊讶。签名为f(T&&的函数如何与类型为T&&的调用不匹配? 更让我惊讶的是调用f(static_cast(t))实际上会调用右值重载f(t&&) 什么是使这成为可能的C++规则?T&是否不仅仅是一个类型?在g()T中是一个命名变量。所有命名变量都是左值。如果T是模板类型,则可以使用将变量转发到f()。这将调用与传递给g() 自动视为右值的对象是没有

假设一个函数有两个重载
f
f(T&&)
f(T&)
。 然后在
g
的正文中:
g(T&&T){f(T);}
将调用重载
f(T&)
,因为T被视为左值

这让我很惊讶。签名为
f(T&&
的函数如何与类型为
T&&
的调用不匹配? 更让我惊讶的是调用
f(static_cast(t))
实际上会调用右值重载
f(t&&)

什么是使这成为可能的C++规则?
T&
是否不仅仅是一个类型?

g()
T
中是一个命名变量。所有命名变量都是左值。如果
T
是模板类型,则可以使用将变量转发到
f()。这将调用与传递给
g()


自动视为右值的对象是没有名称的对象,以及(很快)将没有名称的对象(在返回值的情况下)

T&&T
有一个名称,它是
T

右值之所以是这些东西,是因为在使用点之后引用它们几乎是不可能的

T&
是类型R值引用。右值引用只能绑定到右值(不涉及
static\u cast
),但它是右值引用类型的左值

它是rvalue引用类型的事实只在其构造过程中起作用,如果您执行
decltype(variable\u name)
。否则,它只是引用类型的另一个左值

std::move(t)
执行
返回静态施法(t)
并返回一个右值引用

<> P>规则的规则是用C++标准中的标准化方法编写的。复制/粘贴它们不会有多大用处,因为它们不那么容易理解

第一条一般规则是,当您从函数返回命名值时,或者当值没有名称时,或者当函数显式返回右值引用时,您将获得隐式移动(也称为绑定到右值引用参数的参数)

其次,只有右值引用和常量&
可以绑定到右值

第三,当直接绑定到构造函数外部的引用时,会对临时值进行引用生存期扩展。(因为只有右值引用和常量&可以直接绑定到临时值,所以这仅适用于它们)

第四,
T&
并不总是右值引用。如果
T
类型为
X&
X常量&
,则引用折叠将
T&
转换为
X&
X常量&


最后,类型推断上下文中的
T&
T
推断为
X
X&
X常量&
X常量&&
,这取决于参数的类型,因此可以充当“转发引用”。

按名称引用变量时,始终会得到一个左值。此规则没有例外,但请注意,它不适用于预处理器宏、枚举器或非类型模板参数,它们都不是通常意义上的变量

我认为,虽然这种行为起初似乎没有道理,但当你仔细考虑时,它确实是有意义的,是正确的行为。首先,我们应该注意到,价值范畴显然是表达式的属性,而不是对象本身的属性。这是显而易见的,因为
std::move
从不创建新对象,而只是创建一个引用给定对象的右值表达式。那么我们应该明白:

  • 如果表达式是左值,则通常意味着该表达式引用的对象的值可以或将在同一范围内通过同一表达式访问。这是通过命名变量访问对象时的默认假设
  • 如果表达式是右值,则通常意味着该表达式引用的对象的值以后不能或不会通过同一范围内的同一表达式进行访问。(这包括prvalue临时变量;
    T{}
    在一个表达式中与后面表达式中的
    T{}
    不同;两者都创建匿名对象,但都是不同的,因此后者不能访问与前者相同的对象。)
因此,引用对象的表达式的值类别是相对的;这取决于特定的表达式和范围
std::move
表示您不想在同一范围内再次访问对象的值,从而允许被调用函数将其值移出该对象。但是,当被调用函数访问右值引用的名称时,该值在函数调用期间是永久的函数可以在任何点将值移出该对象,也可以根本不移出,但无论如何,它可能在主体内访问该值,在参数初始化后

在本例中:

void f(Foo&& foo) { /* use foo */ }
void g() {
    Foo foo;
    f(std::move(foo));
}
虽然
g
中的
std::move(foo)
和被调用方中的param
foo
引用同一个对象,但该对象的值将在
g
中的
std::move
点处消失,而在
f
中,该对象的值预计将通过
foo
访问,可能在
f
结束前多次

调用ref限定成员函数时也存在类似情况

struct Foo {
    void f() &;
    void f() &&;
    void g() && {
        f(); // calls lvalue-qualified f
    }
};
void h() {
    Foo().g();
}
这里,
Foo()
的值即将从
h()
中消失;在完整表达式之后,它将不可用。但是,在
Foo::g()
的主体中,它是永久的,直到
g()
结束<代码>*此可可靠地访问同一对象的值。的确如此
void f(Foo&& foo) { /* use foo */ }
void g() {
    Foo foo;
    f(std::move(foo));
}
struct Foo {
    void f() &;
    void f() &&;
    void g() && {
        f(); // calls lvalue-qualified f
    }
};
void h() {
    Foo().g();
}