我有这门课:
class Foo {
public:
Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&& ) {cout << "constructed by rvalue reference." << endl; }
};
Run Code Online (Sandbox Code Playgroud)
然后我插入一个向量:
Foo foo{};
vf.push_back(foo);
Run Code Online (Sandbox Code Playgroud)
输出令人惊讶:
constructed by lvalue reference.
constructed by lvalue reference.
Run Code Online (Sandbox Code Playgroud)
我假设它在传递参数时被复制,所以我试过:
vf.push_back(move(foo));
Run Code Online (Sandbox Code Playgroud)
和
vf.push_back(forward<Foo>(foo));
Run Code Online (Sandbox Code Playgroud)
由于移动语义但仍然调用构造函数两次,输出略有不同:
constructed by rvalue reference.
constructed by lvalue reference.
Run Code Online (Sandbox Code Playgroud)
为什么构造函数被调用两次?它会影响多少性能?我怎么能避免这个?
我在Windows Vista上使用mingw-gcc-4.7.1
总例子:
#include <iostream>
#include <vector>
using namespace std;
class Foo {
public:
Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&& ) {cout << "constructed by rvalue reference." << endl; }
};
int main(int argc, char **argv, char** envp)
{
vector<Foo> vf;
cout << "Insert a temporary." << endl;
vf.emplace_back(Foo{});
Foo foo{};
cout << "Insert a variable." << endl;
vf.emplace_back(foo);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
确切的输出:
Insert a temporary.
constructed by rvalue reference.
Insert a variable.
constructed by lvalue reference.
constructed by lvalue reference.
Run Code Online (Sandbox Code Playgroud)
小智 14
在向量中插入新项时,向量可能必须分配更多内存以适合这些对象.当发生这种情况时,它需要将其所有元素复制到新的内存位置.那将调用复制构造函数.因此,当您插入元素时,您将获得该元素的构造函数以及复制前一个元素时的构造函数.
vector<Foo> vf;
cout << "Insert a temporary." << endl;
vf.emplace_back(Foo{});
Run Code Online (Sandbox Code Playgroud)
上面发生的Foo是创建临时.
然后,该临时被用来构建一个Foo内vector.因此,"由右值参考构建"是您所要求的.
如果您希望简单地构建Foo到位,请尝试:
vs.emplace_back();
Run Code Online (Sandbox Code Playgroud)
下一个:
Foo foo{};
cout << "Insert a variable." << endl;
vf.emplace_back(foo);
Run Code Online (Sandbox Code Playgroud)
在这里你构建一个非临时的foo.然后,您指示std::vector在列表末尾构造一个新元素.
有趣的是,你通过左值引用得到两个构造.第二个似乎是由调整大小引起的.为什么调整大小使你的左值参考construced而不是右值引用,是一招:如果你的move构造函数没有标记noexcept,std::vector倒在副本而不是move!
这是一个说明上述原则的实例:
#include <iostream>
#include <vector>
using namespace std;
class Foo {
public:
Foo() {}
virtual ~Foo() {}
Foo(const Foo&){cout << "constructed by lvalue reference." <<endl; }
Foo(Foo&){cout << "constructed by non-const lvalue reference." <<endl; }
Foo(Foo&& ) noexcept {cout << "constructed by rvalue reference." << endl; }
};
int main(int argc, char **argv, char** envp)
{
vector<Foo> vf;
cout << "Insert a temporary. One move:" << endl;
vf.emplace_back(Foo{});
cout << "Insert a temporary(2). Two moves:" << endl;
vf.emplace_back(Foo{});
cout << "Resize with temporary(3). Two moves:" << endl;
vf.resize(10);
vector<Foo> vf2;
Foo foo{};
cout << "Insert a variable. One copy:" << endl;
vf2.emplace_back(foo);
cout << "Insert a variable(2). One move, one copy:" << endl;
vf2.emplace_back(foo);
cout << "Resize with variable(3). Two moves:" << endl;
vf2.resize(10);
vector<Foo> vf3;
cout << "Insert a nothing. No copy or move:" << endl;
vf3.emplace_back();
cout << "Insert a nothing(2). One move:" << endl;
vf3.emplace_back();
cout << "Resize with nothing(3). Two moves:" << endl;
vf3.resize(10);
}
Run Code Online (Sandbox Code Playgroud)