在以下示例中,即使发出临时对象,也会最终执行复制:
// test.cpp
#include <iostream>
#include <vector>
#include <utility>
#include <initializer_list>
using namespace std;
class mystr : public string
{
public:
mystr() = default;
mystr(const char* c) : string(c) { }
mystr(mystr& m) : string(m) { cout << "& " << c_str() << endl; }
mystr(mystr const & m) : string(m) { cout << "const & " << c_str() << endl; }
mystr(mystr&& m) : string(move(m)) { cout << "&& " << c_str() << endl; }
};
class myvector
{
public:
myvector(initializer_list<mystr> i)
{
cout << "Inside constructor" << endl;
v = vector<mystr>(i);
}
private:
vector<mystr> v;
};
int main()
{
mystr a("hello");
myvector v{ a, mystr("bye"), a, mystr("sorry") };
}
输出:
$ g++ --version | grep "g++"
g++ (Ubuntu/Linaro 4.7.2-2ubuntu1) 4.7.2
$ g++ -std=c++11 test.cpp
$ ./a.out
& hello
& hello
Inside constructor
const & hello
const & bye
const & hello
const & sorry
复制“命名”对象并省略临时对象,但是,一旦构造初始化列表,就会在其他时间复制所有元素:初始化列表删除了对象原始性质的信息。使用-fno-elide-constructors
,这个过程更容易理解:
$ g++ -std=c++11 -fno-elide-constructors test.cpp
$ ./a.out
& hello
&& hello
&& bye
&& bye
& hello
&& hello
&& sorry
&& sorry
Inside constructor
const & hello
const & bye
const & hello
const & sorry
临时对象被移动两次,持久对象在到达构造函数之前被复制和移动,这意味着,在创建初始化列表的过程中。一旦进入构造函数,列表中的所有对象都是构造函数的持久性,因此,它们都被复制了。
是否可以避免此行为(转发)?
我认为,容器应该始终将对象(而不是复制)从初始化列表移动到它自己的容器,因为在构建初始化列表时,对象已经被复制了一次。为什么要连续两次复制?