假设我有一个函数f
的两个重载
void f(T&&); // #1
void f(T&); // #2
然后在另一个函数g
的主体中
void g(T&& t)
{
f(t); // calls #2
}
由于t
被视为左值,因此将调用重载f(T&)
。
这对我来说非常令人惊讶。为什么签名为f(T&&)
的函数不能匹配类型为T&&
的调用?更让我惊讶的是,调用f(static_cast<T&&>(t))
实际上会调用rvalue重载f(T&&)
。
使这成为可能的C++规则是什么?T&&
不仅仅是一种类型吗?
发布于 2015-09-17 08:32:22
自动被视为右值的对象是没有名称的对象,以及(很快)不会有名称的对象(在返回值的情况下)。
T&& t
有一个名字,它是t
。
rvalues之所以是这些东西,是因为在该使用点之后引用它们几乎是不可能的。
T&&
是类型右值引用。右值引用只能绑定到右值(不涉及static_cast
),但在其他情况下它是右值引用类型的左值。
它是右值引用类型的事实只关系到它的构造过程,如果你使用decltype(variable_name)
的话。否则它只是另一个引用类型的左值。
std::move(t)
执行return static_cast<T&&>(t);
并返回右值引用。
管理这一点的规则是用C++标准中的标准行话编写的。复制/粘贴它们并不是那么有用,因为它们并不那么容易理解。
第一个一般规则是,当您从函数返回命名值时,或者当值没有名称时,或者当函数显式返回右值引用时,您将获得隐式移动(也称为绑定到右值引用参数的参数)。
其次,只有右值引用和const&
可以绑定到右值。
第三,当直接绑定到构造函数外部的引用时,会发生临时值的引用生存期扩展。(因为只有右值引用和const&
可以直接绑定到临时的,这只适用于它们)
第四,T&&
并不总是右值引用。如果T
的类型为X&
或X const&
,则引用折叠会将T&&
转换为X&
或X const&
。
最后,类型推导上下文中的T&&
将根据参数的类型将T
推断为X
、X&
、X const&
或X const&&
,因此可以充当“转发引用”。
发布于 2015-09-17 09:01:50
当你通过名称引用一个变量时,你总是得到一个左值。这条规则没有例外,但请注意,它不适用于预处理器宏、枚举器或非类型模板参数,这些参数都不是通常意义上的变量。
我认为,虽然这种行为一开始似乎没有意义,但当你仔细考虑它时,它确实是有意义的,是正确的行为。首先,我们应该注意到,值类别显然是表达式的属性,而不是对象本身的属性。这是显而易见的,因为std::move
从不创建新对象,而只是创建一个引用给定对象的右值表达式。那么我们应该理解这一点:
T{}
与后一个表达式中的T{}
不同;两者都创建匿名对象,但两者都是不同的,因此后者不会访问与前者相同的对象。)因此,引用对象的表达式的值类别是相对的;它取决于特定的表达式和作用域。std::move
表示您不打算在同一范围内再次访问对象的值,从而允许被调用的函数将其值移出该对象。然而,当被调用的函数访问右值引用的名称时,该值在函数调用期间是永久的;该函数可以在任何点将该值移出该对象,或者根本不移出该对象,但无论如何,它可能会在主体内访问它,这是在初始化参数之后的。
在此示例中:
void f(Foo&& foo) { /* use foo */ }
void g() {
Foo foo;
f(std::move(foo));
}
尽管g
中的std::move(foo)
和被调用者中的参数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()
的末尾;*this
可靠地访问同一对象的值。因此,当g()
调用f()
时,它自然应该调用期望左值的重载,并且f()
不应该从<代码>D35窃取*this*
的值,因为<代码>D36可能仍然想要访问它。
发布于 2015-09-17 08:31:19
在g()
中,t
是一个命名变量。所有命名的变量都是左值。如果T
是模板类型,那么可以使用std::forward
将变量转发给f()
。这将调用与传递给g()
的类型相同的f()
template<typename T>
g(T&& t) { f(std::forward<T>(t));}
如果T
不是模板类型,而只是一种类型,那么您可以使用std::move
g(T&& t) { f(std:move(t)); }
https://stackoverflow.com/questions/32620750
复制相似问题