我做错了什么(又一次)?
#include <iostream>
using std::cout;
struct Map
{
Map()
{
cout << "Map()\n";
}
Map(const Map& pattern)
{
cout << "Map(const Map& pattern)\n";
}
Map(Map&& tmp)
{
cout << "Map(Map&& tmp)\n";
}
};
Map createMap()
{
return Map();
}
int main(int argc, char* argv[])
{
//dflt
Map m;
//cpy
Map m1(m);
//move
Map m2(Map(m1));//<<I thought that I create here tmp unnamed obj.
Map m3(createMap());//<<---or at least here, but nope...
return 0;
}
请参阅代码中的注释行
已编辑[摘自FredOverflow答案]
int main()
{
std::cout << "default\n";
Map m;
std::cout << "\ncopy\n";
Map m1(m);
std::cout << "\nmove\n";
Map m2((Map(m1)));
std::cout << "\nmove\n";
Map m3(createMap());
}
我收到了输出:
default
Map()
copy
Map(const Map& pattern)
move
Map(const Map& pattern)//Here why not move ctor aswell as copy?
move
Map()
Map()
Map(Map&& tmp)
答案 0 :(得分:4)
Map m3(createMap());//<<---or at least here, but nope...
您正在看到返回值优化的实际效果。在C ++中,允许编译器优化复制返回的对象,并让函数直接与存储结果的调用者对象一起工作。也没有必要调用移动构造函数。
使函数更复杂,因此编译器无法使用优化,您将看到移动操作。例如:
Map createMap()
{
Map a, b;
if (rand())
return a;
return b;
}
答案 1 :(得分:3)
你宣布一个功能,而不是一个对象:
T name (T(blah));
相当于:
T name(T blah);
可以识别为函数声明。你可以使用额外的parens:
Map m2 ((Map(m1)));
答案 2 :(得分:1)
我稍微修改了您的main
例程以更好地理解输出:
int main()
{
std::cout << "default\n";
Map m;
std::cout << "\ncopy\n";
Map m1(m);
std::cout << "\nmove\n";
Map m2(Map(m1));
std::cout << "\nmove\n";
Map m3(createMap());
}
这是g++ -fno-elide-constructors
的输出:
default
Map()
copy
Map(const Map& pattern)
move
move
Map()
Map(Map&& tmp)
Map(Map&& tmp)
正如其他人已经指出的那样,Map m2(Map(m1));
确实是一个函数声明,因此你没有输出。第二步是不被解释为函数声明,因为createMap
不是类型名称。这里涉及两个移动构造函数。一个人将通过评估Map()
创建的临时对象移动到通过评估createMap()
创建的临时对象,第二个移动从后者初始化m3
。这正是人们所期望的。
如果您通过编写Map m2((Map(m1)));
来修复第一步,则输出变为:
move
Map(const Map& pattern)
Map(Map&& tmp)
同样,没有惊喜。通过评估Map(m1)
来调用复制构造函数,然后将该临时对象移动到m2
。如果您在没有-fno-elide-constructors
的情况下进行编译,则移动操作会消失,因为它们会被更有效的优化(如RVO或NRVO)取代:
default
Map()
copy
Map(const Map& pattern)
move
Map(const Map& pattern)
move
Map()
我确信Visual C ++有一个类似于-fno-elide-constructors
的编译器选项,您可以使用它来更好地理解移动语义。
答案 3 :(得分:1)
此程序显示预期输出。
#include <iostream>
using std::cout;
struct Map
{
Map()
{
cout << "Map()\n";
}
Map(const Map& pattern)
{
cout << "Map(const Map&)\n";
}
Map(Map&& tmp)
{
cout << "Map(Map&&)\n";
}
};
Map createMap()
{
Map m;
return m;
}
int main(int argc, char* argv[])
{
//dflt
Map m;
//cpy
Map m1(m);
//move
Map m2 = createMap();//<<I thought that I create here tmp unnamed obj.
std::cin.get();
return 0;
}
请注意对createMap()的更改。它不使用直接临时值,而是使用命名返回值。此程序显示Visual Studio 2010上的预期输出。
答案 4 :(得分:0)
Map createMap()
{
return Map();
}
我认为编译器会在上面做一个RVO (return value optimization),因此不会创建临时版。
如果您将其更改为以下内容,您应该会看到您的移动ctor被调用。
Map createMap()
{
Map m;
m.DoSomething(); // this should make the compiler stop doing RVO
return m;
}