我制作了一个测试程序来测试一些移动语义,请参见下文。
struct testcd
{
int x;
testcd(int i) : x(i) { std::cout << "constructor for " << i << "\n"; }
~testcd() { std::cout << "destructor for " << x << "\n"; }
testcd(const testcd& cd) { std::cout << "copy constructor\n"; x = cd.x; }
testcd(testcd&& cd) { std::cout << "move constructor\n"; x = cd.x; }
testcd& operator=(const testcd& cd) { std::cout << "copy operator\n"; x = cd.x; return *this; }
testcd& operator=(testcd&& cd) { std::cout << "move operator\n"; x = cd.x; return *this; }
};
testcd TestCD()
{
std::cout << "- enter" << std::endl;
std::vector <testcd> vec;
std::cout << "- after empty vector" << std::endl;
{
testcd s(2);
vec.emplace_back(s);
}
std::cout << "- after emplace back" << std::endl;
{
testcd t(1);
vec.push_back(t);
}
std::cout << "- after push back" << std::endl;
for (auto&& t : vec)
{
std::cout << t.x << " ";
}
std::cout << "\n- after for &&" << std::endl;
testcd c1(10);
return c1;
}
int main()
{
{
auto cd = TestCD();
std::cout << cd.x << std::endl;
}
_getch();
return 0;
}
输出在这里:
- enter
- after empty vector
constructor for 2
copy constructor
destructor for 2
- after emplace back
constructor for 1
copy constructor
copy constructor
destructor for 2
destructor for 1
- after push back
2 1
- after for &&
constructor for 10
destructor for 2
destructor for 1
10
destructor for 10
有些事情仍然让我感到困惑:
push_back和emplace_back看起来非常相似,复制构造函数用于两者。两者之间实际上有区别吗?
似乎在推送到容器(std :: vector)时,没有调用移动语义。唯一的方法是通过调用来强制执行此操作
vec.push_back(std::move(t));
编译器可以看到临时值超出范围而没有被使用。
返回似乎按预期执行(某种)。没有优化,将调用move构造函数。通过优化,似乎只创建了一个对象,这两个方法都共享该对象,而没有移动或复制任何内容。但是,如果我返回使用std :: move,则会销毁Release优化,并且会额外调用move构造器+构造器+析构器。
所以我想知道:语言为什么不一致:推向向量要求我调用move()
,返回要求我不要调用move()
。看来我需要在每个编译器和每个用例中都进行测试,以便确保可以编写简单的最佳代码,并且这对初学者非常容易产生误导。
代码是使用VS2017 C ++ 17编译的,已在Release和Debug上进行了测试。