我正在阅读有关右值的内容,并在编译器资源管理器中尝试代码片段时遇到了一些问题。这是一个人为的例子:
class A
{
public:
A&& func(A& rhs)
{
//return rhs;
return std::move(rhs); //returning A&&
}
};
对比
class A
{
public:
A func(A& rhs)
{
//return rhs;
return std::move(rhs); //returning A&&
}
};
我原本以为第二个代码片段会因为返回类型不匹配而编译失败。Func 的返回类型是 ,A
而它实际返回的是A&&
。std::move()
我尝试了最新版本的 gcc、clang 和 msvc。它们都具有相同的行为。有人能解释一下这里发生了什么吗?
11
最佳答案
2
在 C++ 中,当您std::move
对对象使用时,您实际上并没有将其转换为右值引用 ( T&&
)。相反,您将其强制转换为右值,这向编译器发出信号,它可以将该对象视为临时对象,并且如果可能的话,可以从中移动而不是复制。但是,std::move
不会改变基础类型;它只是为编译器提供使用移动语义的提示。
以下是两个例子中发生的情况:
示例 1:A&& func(A& rhs)
在此版本中,函数func
返回一个A&&
(对 的右值引用A
)。当您返回 时std::move(rhs)
,您直接返回一个右值引用,它与返回类型 匹配A&&
。这是有道理的,并且会按预期进行编译。
示例 2:A func(A& rhs)
在这种情况下,函数的func
返回类型为A
,它是一个值(而不是右值引用)。但是,当您返回 时std::move(rhs)
,您仍然会转换rhs
为右值引用(A&&
)。这就是为什么它仍然编译并表现如您所观察到的那样:
-
自动转换:当您指定 (a value) 的返回类型时
A
,编译器期望创建并返回一个A
对象。如果您提供A&&
(via ),编译器会将其解释为移动构造函数而不是复制它的std::move(rhs)
指令。编译器认为这是一个右值并使用移动构造函数来构造返回值。A
rhs
rhs
-
返回类型推导和临时实现:如果返回类型为按值,则编译器将在返回右值时隐式执行移动。在这种情况下,
A func(A& rhs)
指定返回类型为A
,因此编译器将其视为右值,并使用移动构造函数std::move(rhs)
创建一个临时值。A
7
-
1“当您对对象使用 std::move 时,您实际上并没有将其转换为右值引用 (T&&)。“
std::move
实际上只是强制转换为T&&
。 它以函数形式提供的唯一原因,而不是在代码中明确进行强制转换,是为了避免重复类型T
。
– -
@jayashankar 强制转换意味着强制编译器将一种类型解释为另一种类型。除非转换非法,否则编译器将继续使用新的(转换)类型。从逻辑上讲,我认为语法检查中的静态类型检查将首先进行。编译器是否应该调用移动或复制构造函数或 RVO,应该稍后进行?
– -
1@ArthurTacca 答案是正确的。表达式永远没有引用类型,因此
std::move
只会将值类别更改为右值,而不会更改类型。
– -
@HolyBlackCat 即使从语言律师的角度来看,它在技术上是正确的,但我认为它在这样的介绍/摘要中具有误导性。他们说“你实际上并没有将它转换成……
T&&
”对于字面上定义为的东西static_cast<T&&>
。
– -
2更重要的是,这种区别(如果有的话)在这里并不重要。这个问题的关键在于,C++ 允许从
x
in隐式转换return x
为实际返回的值,因此它是使用构造函数从T&&
(或右值到T
或任何你想叫它的名字)转换为实际T
值(尽管可以省略复制/移动构造)。
–
|
我预计第二个代码片段会因为返回类型不匹配而编译失败。
这里没有有问题的不匹配。
在第二种情况下,函数返回类型为 的右值A
。此返回值将与 的移动构造函数
一起使用,以在调用方端构造实例。A
A
下面证明了这一点:
#include <iostream>
class A {
public:
A() { std::cout << "default ctor\n"; };
~A() { std::cout << "dtor\n"; };
A([[maybe_unused]] A const & a) { std::cout << "copy ctor\n"; };
A([[maybe_unused]] A && a) { std::cout << "move ctor\n"; };
// To respect the rule of 5 we also need copy and move operator=,
// but it is omitted here for the example
static A func(A& rhs) {
//return rhs;
return std::move(rhs); //returning A&&
}
};
int main() {
A a1;
A a2 = A::func(a1);
}
输出:
default ctor
move ctor
dtor
dtor
如果您不提供移动构造函数,编译器将会生成它。
|
–
A&&
,而是返回了 类型的右值A
。–
float one() { return 1; }
?也就是说,一个函数返回一个float
,但return
语句被赋予了不同的类型(一个int
值)来返回?–
–
explicit
决定了是否允许隐式转换。(试一试:标记移动构造函数explicit
将导致你的第二个代码片段编译失败。)–
|