【发布时间】:2021-03-30 22:29:35
【问题描述】:
我遇到了一些关于右值返回的行为,我无法理解。 假设我们有以下结构:
struct Bar
{
int a;
Bar()
: a(1)
{
std::cout << "Default Constructed" << std::endl;
}
Bar(const Bar& Other)
: a(Other.a)
{
std::cout << "Copy Constructed" << std::endl;
}
Bar(Bar&& Other)
: a(Other.a)
{
std::cout << "Move Constructed" << std::endl;
}
~Bar()
{
std::cout << "Destructed" << std::endl;
}
Bar& operator=(const Bar& Other)
{
a = Other.a;
std::cout << "Copy Assigment" << std::endl;
return *this;
}
Bar& operator=(Bar&& Other) noexcept
{
a = Other.a;
std::cout << "Move Assigment" << std::endl;
return *this;
}
};
struct Foo
{
Bar myBar;
Bar GetBar()
{
return myBar;
}
// Note that we are not returning Bar&&
Bar GetBarRValue()
{
return std::move(myBar);
}
Bar&& GetBarRValueExplicit()
{
return std::move(myBar);
}
};
如下使用:
int main()
{
Foo myFoo;
// Output:
// Copy Constructed
Bar CopyConstructed(myFoo.GetBar());
// Output:
// Move Constructed
Bar MoveConstructedExplicit(myFoo.GetBarRValueExplicit());
// Output:
// Move Constructed
//
// I don't get it, GetBarRValue() has has the same return type as GetBar() in the function signature.
// How can the caller know in one case the returned value is safe to move but not in the other?
Bar MoveConstructed(myFoo.GetBarRValue());
}
现在我明白为什么Bar MoveConstructedExplicit(myFoo.GetBarRValueExplicit()) 调用移动构造函数了。
但由于函数Foo::GetBarRValue() 没有显式返回Bar&&,我希望它的调用给出与Foo::GetBar() 相同的行为。我不明白在这种情况下为什么/如何调用移动构造函数。据我所知,没有办法知道GetBarRValue() 的实现将myBar 转换为rValue 引用。
我的编译器是否在对我进行优化(在 Visual Studio 的调试版本中对此进行测试,显然无法禁用返回值优化)?
我觉得有点令人沮丧的是,调用方的行为可能会受到GetBarRValue() 的实现的影响。 GetBarRValue() 签名中的任何内容都没有告诉我们,如果调用两次,它将给出未定义的行为。在我看来,正因为如此,当函数没有显式返回 && 时,return std::move(x) 是不好的做法。
有人可以向我解释这里发生了什么吗?谢谢!
【问题讨论】:
-
FWIW,如果编译器可以看到定义(即,它在同一个源文件中,或者在头文件中),那么编译器可以知道它做了一个
std::move -
一开始我还以为是内联,但即使在单独的 .h 和 .cpp 文件中声明和定义,行为也是一样的。
标签: c++ move-semantics rvalue copy-elision