低效率 std::move()?
low efficient std::move()?
请看下面的代码片段,似乎 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 生成一个简单的移动构造函数,这个简单的移动构造函数将采用 copy semantic.
那么我的问题是:
- std::move() 对于这种情况是完全多余的。
- 此外,如果 std::move() 的参数有一个普通的移动构造函数,那么 std::move() 就是多余的。
- 如果普通移动构造函数执行与普通复制构造函数相同的操作,为什么编译器生成不同的反汇编?其实这是最让我困惑的。
- std::move() is completely redundant for this case.
技术上不是问题,但是是的,这是正确的。
- Moreover, if the parameter of std::move() has a trivial move constructor, then std::move() is redundant.
同上
- if the trivial move constructor performs the same action as the trivial copy constructor, why the compiler generates different disassembly?
可能是因为你在一个中调用了函数 std::move
而在另一个中没有。
但它不必生成不同的程序集,因为可观察到的行为是相同的。我的编译器生成相同的程序集。
嗯,我想对这个问题做一个总结。
如果移动构造函数是微不足道的(由编译器生成),则 std::move() 仅采用按位复制。这意味着 vec.emplace_back(std::move(b)) 等同于 vec.emplace_back(b).
以下是显示差异的另一个更好的示例。我们尝试用移动语义构造成员 _x 和 _y。因为 X 有一个用户声明的移动构造函数而 Y 没有,所以 X 的移动构造函数被调用,Y 的复制构造函数被调用。
class X {
public:
X() { cout << "X()" << endl; }
X(X const&) { cout << "X(X const&)" << endl; }
X(X&&) noexcept { cout << "X(X&&)" << endl; }
};
class Y {
public:
Y() { cout << "Y()" << endl; }
Y(Y const&) { cout << "Y(Y const&)" << endl; }
};
struct Box {
Box(X&& x, Y&& y) : _x(std::move(x)), _y(std::move(y)) {}
X _x;
Y _y;
};
int main() {
X x;
Y y;
Box bb(std::move(x), std::move(y));
return 0;
}
/*
output:
X()
Y()
X(X&&)
Y(Y const&)
*/
请看下面的代码片段,似乎 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 生成一个简单的移动构造函数,这个简单的移动构造函数将采用 copy semantic.
那么我的问题是:
- std::move() 对于这种情况是完全多余的。
- 此外,如果 std::move() 的参数有一个普通的移动构造函数,那么 std::move() 就是多余的。
- 如果普通移动构造函数执行与普通复制构造函数相同的操作,为什么编译器生成不同的反汇编?其实这是最让我困惑的。
- std::move() is completely redundant for this case.
技术上不是问题,但是是的,这是正确的。
- Moreover, if the parameter of std::move() has a trivial move constructor, then std::move() is redundant.
同上
- if the trivial move constructor performs the same action as the trivial copy constructor, why the compiler generates different disassembly?
可能是因为你在一个中调用了函数 std::move
而在另一个中没有。
但它不必生成不同的程序集,因为可观察到的行为是相同的。我的编译器生成相同的程序集。
嗯,我想对这个问题做一个总结。
如果移动构造函数是微不足道的(由编译器生成),则 std::move() 仅采用按位复制。这意味着 vec.emplace_back(std::move(b)) 等同于 vec.emplace_back(b).
以下是显示差异的另一个更好的示例。我们尝试用移动语义构造成员 _x 和 _y。因为 X 有一个用户声明的移动构造函数而 Y 没有,所以 X 的移动构造函数被调用,Y 的复制构造函数被调用。
class X {
public:
X() { cout << "X()" << endl; }
X(X const&) { cout << "X(X const&)" << endl; }
X(X&&) noexcept { cout << "X(X&&)" << endl; }
};
class Y {
public:
Y() { cout << "Y()" << endl; }
Y(Y const&) { cout << "Y(Y const&)" << endl; }
};
struct Box {
Box(X&& x, Y&& y) : _x(std::move(x)), _y(std::move(y)) {}
X _x;
Y _y;
};
int main() {
X x;
Y y;
Box bb(std::move(x), std::move(y));
return 0;
}
/*
output:
X()
Y()
X(X&&)
Y(Y const&)
*/