Mar*_* Ba 5 c++ copy-elision std-pair visual-c++-2012
我注意到Visual Studio 2012中出现了一些非常奇怪的事情:定义一对像这样的对象:
auto objp = pair<int, LogMe>();
Run Code Online (Sandbox Code Playgroud)
会不会的Elid一对在VC11的复制/移动,这个调用会打印:
LogMe::LogMe - def.ctor!
LogMe::LogMe - move.ctor!
LogMe::~LogMe - dtor!
Run Code Online (Sandbox Code Playgroud)
也就是说,将创建一个临时对,然后将其移动到objp变量中.(声明pair<...> obj;它只记录默认的ctor)
我单独使用LogMe测试对象进行交叉检查:
cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();
# Construct Object via auto obj = ...
LogMe::LogMe - def.ctor!
Run Code Online (Sandbox Code Playgroud)
这里的任务将被删除.
这似乎是VC11特有的,因为在IDEOne(使用gcc 4.8.1)中测试它表明,无关紧要的移动总是在那里被省略.
这里发生了什么? 不能依赖被删除的初始化副本让我感到紧张.
注意:发布与调试版本的测试显示相同的结果.(我原本期望,因为复制省略与MSVC中的优化标志无关.)
要测试的完整源代码(另请参阅ideone链接):
#include "stdafx.h"
#include <iostream>
#include <map>
using namespace std;
struct LogMe {
std::string member;
LogMe() {
cout << __FUNCTION__ << " - def.ctor!" << endl;
}
~LogMe() {
cout << __FUNCTION__ << " - dtor!" << endl;
}
LogMe(LogMe const&) {
cout << __FUNCTION__ << " - cpy.ctor!" << endl;
}
LogMe& operator=(LogMe const&) {
cout << __FUNCTION__ << " - cpy.assign.op!" << endl;
return *this;
}
LogMe(LogMe&&) {
cout << __FUNCTION__ << " - move.ctor!" << endl;
}
LogMe& operator=(LogMe&&) {
cout << __FUNCTION__ << " - move.assign.op!" << endl;
return *this;
}
};
int _tmain(int argc, _TCHAR* argv[])
{
{
cout << "# Construct Object via auto obj = ...\n";
auto obj = LogMe();
cout << "# Construct pair<int, object> via auto objp = ...\n";
auto objp = pair<int, LogMe>();
cout << "# Construct pair<int, object> via pair objp2; ...\n";
pair<int, LogMe> p2;
}
return 0;
Run Code Online (Sandbox Code Playgroud)
看来不是移动向量,也不是模板化移动向量导致了问题,而是enable_if<is_convertable<...模板化移动向量中存在:
仅使用一个对象进行测试,抛出auto并pair退出测试:
好的,复制/移动已删除:
cout << "# Construct Object: auto obj = LogMe();\n";
LogMe obj = LogMe();
LogMe(LogMe&&) {
cout << __FUNCTION__ ...
}
Run Code Online (Sandbox Code Playgroud)并且,通过这样的测试:
cout << "# Construct Object: LogMeTempl obj = LogMeTempl();\n";
LogMeTempl obj = LogMeTempl();
cout << "# Construct Object: LogMeTempl obj2;\n";
LogMeTempl obj2;
Run Code Online (Sandbox Code Playgroud)
好的,复制移动也被省略了:
template<class Other>
LogMeTempl(Other&& rhs
// , typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
) {
cout << __FUNCTION__ << ...;
}
Run Code Online (Sandbox Code Playgroud)失败!调用移动ctor!
template<class Other>
LogMeTempl(Other&& rhs
, typename enable_if<is_convertible<Other, LogMeTempl>::value, void>::type ** = 0
) {
cout << __FUNCTION__ << ...;
}
Run Code Online (Sandbox Code Playgroud)
请注意,enable_if 可以减少为enable_if<true, void>::type** = 0- 如果事实上任何其他默认参数都可以(例如, int defaulted_param_on_move_ctor = 0,它仍然会阻止 move elision)。
这也扩展到仅具有默认参数的复制因子的类型。也不会被删掉。与 gcc 的快速交叉检查表明那里似乎没有任何这样的问题。
在其复制/移动构造函数中具有默认参数的类型不会删除其初始化复制/移动。
我已针对此问题在 MS.connect 上添加了一个错误。
我还在IDEone 中添加了 (N)RVO 的测试用例。即使没有默认参数,*N*RVO 在 gcc 中似乎也比 VC++ 中工作得更好。
| 归档时间: |
|
| 查看次数: |
277 次 |
| 最近记录: |