2

让我们考虑以下 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。

但我不明白为什么abc在优化开关打开时都相等。我可能认为 Microsoft C++ 编译器用确实移动的复制构造函数代替了不执行任何操作的复制构造函数,但我只是不确定。

4

1 回答 1

6

标准部分[conv.lval]规定:

非函数、非数组类型的左值T可以转换为纯右值。如果T是不完整类型,则需要进行此转换的程序格式错误。如果泛左值所指的对象不是类型T的对象,也不是派生自 的类型的对象T或者如果该对象未初始化,则需要此转换的程序具有未定义的行为。如果T是非类类型,则纯右值的类型是 的 cv 非限定版本T。否则,纯右值的类型是T

在内部print,表达式cout << content[i++]使用左值到右值的转换。当您调用print(b)orprint(c)时,转换发生在从未初始化的对象上,因此您有未定义的行为。

试图描述未定义的行为是徒劳的。


注意:对象bc由复制构造函数初始化。复制构造函数不会初始化子对象b.contentor c.content,这意味着这些数组及其所有成员元素在形式上是未初始化的。

a代码在初始化时实际上并没有移动cstd::move创建一个右值引用,这使得移动成为可能,但没有匹配的构造函数接受右值引用,因此使用复制构造函数,a被复制而不是移动。

于 2012-07-04T15:08:48.293 回答