【发布时间】:2012-07-05 02:07:59
【问题描述】:
让我们考虑以下 Microsoft Visual C++(Microsoft Visual Studio 2012 RC,版本 11.0.50522.1 RCREL)中自定义数组的类模板。
/*C++11 switch-on*/
#include <iostream>
template <typename element, unsigned int size>
class array
{
private:
element data[size];
public:
array(){}
~array(){}
array(const array & other)(){}
element & operator [](unsigned int i)
{
if(i<size)
return data[i];
else
throw std::runtime_error("Out of boundary");
}
}
请注意,构造函数、析构函数和复制构造函数被定义为什么都不做。一个普通的打印函数定义如下
/*printing*/
template <typename element, unsigned int size>
void print(test::array<element, size> & content)
{
unsigned int i=0;
for(std::cout<<"["<<content[i++];i<size;std::cout<<content[i++])
std::cout<<",";
std::cout<<"]"<<std::endl;
}
当程序运行以下主程序时
int main(int argc, char * argv[])
{
array<int, 3> a;
/* uniform initialization is not supported yet
* so we bother iterating to assign to initialize
* a to [1,2,3]
*/
for(int i=0;i<3;i++)
a[i]=i+1;
/*copy*/
auto b=a;
/*move*/
auto c=std::move(a);
/*change in a*/
a[0]=0;
print<int, 3>(a);
print<int, 3>(b);
print<int, 3>(c);
return 0;
}
根据编译优化,输出结果会有所不同。特别是,如果我编译并运行
-
打开 /Od 开关
a=[0,2,3]
b=[1470797225,-2,9185596]
c=[0,2620008,9186761]
-
打开 /O1、/O2 或 /Ox
a=[0,2,3]
b=[0,2,3]
c=[0,2,3]
现在我明白了
- 打开 /Od 开关
- b 与 a 不同,因为复制构造函数在调用时什么也不做
- c 与 a 不同,因为复制构造函数在调用时什么也不做。但是根据移动语义,a中数组数据中元素的变化也反映到c中。所以 a[0]==c[0]==0。
但我不明白为什么 a、b 和 c 在优化开关打开的情况下都相等。我可能会认为 Microsoft C++ 编译器会用移动的复制构造函数代替不执行任何操作的复制构造函数,但我不确定。
【问题讨论】:
-
相当肯定 VS2012 RC 是编译器的 17 或 18 版本,而不是 11。
-
无论如何,你有未定义的行为,因为你从一个你从未写过的变量中读取。编译器所做的任何事情都是正确的。
-
a[0]=0 是 UB。您将实例“a”移动到“c”。 (实际上在移动之后对“a”的任何访问)我也能够使用 VS2010 重现该行为,因此我将重新标记它,因为它不特定于 VS2012。
-
@BenVoigt 读取从未写入的变量是什么意思?关键是 a、b 和 c 首先是相等的,这是不应该的,因为复制构造函数什么都不做。
-
@LeSnip3R:不是
a[0] = 0;是UB,是print读取成员从未初始化过。
标签: c++ visual-c++ c++11 visual-studio-2012 move-semantics