Let’s consider following class template of custom array in Microsoft Visual C++ (Microsoft Visual Studio 2012 RC, version 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");
}
}
Note that constructor, destructor and copy constructor are defined to do nothing. A trivial printing function is defined as following
/*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;
}
When program runs the following main
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;
}
the outputs turn out to be different depending on compiling optimization. Particularly, if I compile and run
-
with /Od switch on
a=[0,2,3]
b=[1470797225,-2,9185596]
c=[0,2620008,9186761]
-
with /O1, /O2 or /Ox switch on
a=[0,2,3]
b=[0,2,3]
c=[0,2,3]
Now I understand that
- with /Od switch on
- b is different from a because copy constructor does nothing when called
- c is different from a because copy constructor does nothing when called. But according to move semantic, change of element in array data in a also reflects to c. So a[0]==c[0]==0.
But I don’t understand why a, b and c are all equal with optimization switch on. I might think that Microsoft C++ compiler replaces copy constructor that does nothing with one that does move, but I’m just not sure about it.
Section
[conv.lval]of the Standard decrees that:Inside
print, the expressioncout << content[i++]uses an lvalue-to-rvalue conversion. When you callprint(b)orprint(c), the conversion takes place on an object that has never been initialized, so you have undefined behavior.Trying to characterize undefined behavior is an exercise in futility.
NOTE: Objects
bandcare initialized by the copy constructor. The copy constructor does not initialize the subobjectb.contentorc.content, meaning that these arrays, and all their member elements, are formally uninitialized.The code doesn’t actually move from
awhen initializingc.std::movecreates an rvalue-reference, which makes moving possible, but there is no matching constructor accepting an rvalue-reference, so the copy constructor is used,ais copied and not moved.