经过很长时间的C#工作后,我正在使用C ++ 17,并通过一些Euler问题解决了我的问题。无论如何,任何人都可以解释为什么createRandomVector(const int n)
不“移动”所创建的向量吗?我输出内存地址,并且它们仅在通过引用传递时保持不变(很明显)。下面是代码:
#include <iostream>
#include <vector>
#include <algorithm>
#include <numeric>
#include <random>
using namespace std;
auto createRandomVector(const int n)
{
vector<int> v(n);
iota(begin(v), end(v), 1);
shuffle(begin(v), end(v), std::mt19937());
cout << &v << endl;
return v;
}
void printVector(const vector<int>& v, ostream& os);
int main()
{
auto v = createRandomVector(100);
printVector(v, cout);
cout << endl;
cout << &v << endl;
return 0;
}
void printVector(const vector<int>& v, ostream& os)
{
cout << &v << endl;
for_each(begin(v), end(v), [&os](auto& i)
{
os << i << " ";
});
}
以下是输出:
00BBFC20
00BBFD38
100 73 64 ... 85 90
00BBFD38
为什么第一个内存地址与第二个不匹配?我对移动在现代C ++中的工作方式有一些了解(static_cast)。但是为什么这不起作用?
答案 0 :(得分:5)
这里有两个问题:移动和复制省略。
首先,移动:移动表示存在两个不同的对象,一个对象的内容转移到另一个对象。 (与将一个内容复制到另一个内容相反)。在C ++中,对象在整个生命周期中都有固定的地址。
示例片段:
vector<int> a { 1, 2, 3, 4, 5 };
vector<int> b;
cout << &a << ", " << &a[0] << '\n';
b = std::move(a);
cout << &b << ", " << &b[0] << '\n';
我没有运行此命令,因此希望没有错别字,但是您应该看到,即使两个向量不同,int
对象的块也已从一个转移到另一个。
如果将&v[0]
的输出添加到程序中,您将看到相同的效果。
第二,copy elision。在这种情况下,C ++标准使其成为可选,以便在v
中为createRandomVector
预留的存储空间中实际创建v
中的局部变量main
。在这种情况下,甚至没有与return
步骤相关的移动或复制操作。
可能发生的情况是函数按值返回时,并且return语句的形式为:return X;
,其中X
是不带修饰的局部变量的名称。
如果编译器确实采用了此选项,则程序的前两个输出将相同。显然,您的编译器仅决定在发布模式下执行此操作。
答案 1 :(得分:4)
为什么第一个内存地址与第二个不匹配?
它们是不同的对象,因此不需要它们具有相同的内存地址。
但是,第二个对象可能会重用第一个对象的存储。这种优化-即构造局部自动变量来代替返回值,从而从局部变量中消除其复制初始化(通过移动)-称为命名返回值优化。
返回值本身不过是一个临时值,在调用范围内还有一个从临时值到局部变量的移动。 编辑:从C ++ 17开始不再适用。
因此,您的问题的另一个答案是:因为编译器没有执行或执行命名的返回值优化,或者没有从返回值中忽略移动。
在Debug中,我用
&v
得到了不同的结果,但是在Release模式中,我得到了相同的结果。
对于编译器来说,在调试模式下不执行某些优化是很典型的。
为什么函数返回不移动向量?
在v
是移动构造的意义上,向量是移动的。但是看来您通过“移动”还可以表达其他意思。