【发布时间】:2020-11-23 09:20:54
【问题描述】:
请看下面的sn-p代码,看来std::move()在这种情况下效率很低。
class A {};
struct B {
double pi{ 3.14 };
int i{ 100 };
A* pa{ nullptr };
};
int main() {
B b;
std::vector<B> vec;
vec.emplace_back(b); // 1) without move
vec.emplace_back(std::move(b)); // 2) with move
return 0;
}
我在 Visual Studio 2019 [C++ 14,Release] 中得到了以下反汇编:
vec.emplace_back(b); // 1) without move
00E511D1 push eax
00E511D2 push 0
00E511D4 lea ecx,[vec]
00E511D7 call std::vector<B,std::allocator<B> >::_Emplace_reallocate<B> (0E512C0h)
vec.emplace_back(std::move(b)); // 2) with move
00E511DC mov eax,dword ptr [ebp-18h]
00E511DF cmp eax,dword ptr [ebp-14h]
00E511E2 je main+91h (0E511F1h)
00E511E4 movups xmm0,xmmword ptr [b]
00E511E8 movups xmmword ptr [eax],xmm0
00E511EB add dword ptr [ebp-18h],10h
00E511EF jmp main+9Eh (0E511FEh)
00E511F1 lea ecx,[b]
00E511F4 push ecx
00E511F5 push eax
00E511F6 lea ecx,[vec]
00E511F9 call std::vector<B,std::allocator<B> >::_Emplace_reallocate<B> (0E512C0h)
很容易看出,移动版本需要更多不必要的工作。根据here的描述,编译器将为结构B生成一个普通的移动构造函数,这个普通的移动构造函数将采用复制语义。
那么我的问题是:
- std::move() 在这种情况下是完全多余的。
- 此外,如果 std::move() 的参数具有平凡的移动构造函数,则 std::move() 是多余的。
- 如果普通移动构造函数执行与普通复制构造函数相同的操作,为什么编译器会生成不同的反汇编?实际上,这对我来说是最令人困惑的。
【问题讨论】:
-
看来你的反汇编也有调整矢量大小的代码。您可以尝试提前预订。
-
是的,你是对的。
-
优化编译器会将 main() 的主体优化为“返回 0”。最好使用 godbolt.org 进行此类研究。
-
很高兴知道。我尝试使用 godbolt.org,现在更加困惑。对于非移动函数,称为 void std::vector >::emplace_back(B &);对于移动版本,称为 void std::vector >::emplace_back(B &&)。因此,std::move() 似乎确实有效,因为调用了不同版本的 emplace_back()。
-
B&和B非常不同,不要期望运行相同的代码。此外,如果您的指针做了更有趣的事情,您可能还想编写自己的move构造函数/赋值等。
标签: c++ move-semantics