在Visual C ++ 2010 Beta 2上复制elision

时间:2009-11-07 13:19:28

标签: c++ visual-c++ c++11 visual-studio-2010-beta-2 copy-elision

我正在Want Speed? Pass by Value上阅读C++ Next blog并创建this program以了解复制省略并在C ++ 0x中移动语义:

#include <vector>
#include <iostream>

class MoveableClass {
public:
    MoveableClass() : m_simpleData(0), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (no data)" << std::endl;
    }

    MoveableClass(std::vector<double> data) : m_data(std::move(data)), m_simpleData(0), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (with data)" << std::endl;
    }

    MoveableClass(int simpleData) : m_simpleData(simpleData), instance(++Instances) {
        std::cout << "Construct instance " << instance << " (with simple data)" << std::endl;
    }

    MoveableClass(const MoveableClass& other) 
        : m_data(other.m_data), m_simpleData(other.m_simpleData), instance(++Instances)
    {
        std::cout << "Construct instance " << instance << " from a copy of " << other.instance << std::endl;
        Elided = false;
    }

    MoveableClass(MoveableClass&& other) 
        : m_data(std::move(other.m_data)), m_simpleData(other.m_simpleData), instance(++Instances)
    {
        std::cout << "Construct instance " << instance << " from a move of " << other.instance << std::endl;
        Elided = false;
    }

    MoveableClass& operator=(MoveableClass other) {
        std::cout << "Assign to instance " << instance << " from " << other.instance << std::endl;
        other.Swap(*this);
        return *this;
    }

    ~MoveableClass() {
        std::cout << "Destroy instance " << instance << std::endl;
        --Instances;
    }

    void Swap(MoveableClass& other) {
        std::swap(m_data, other.m_data);
        std::swap(m_simpleData, other.m_simpleData);
    }

    static int Instances;
    static bool Elided;

private:
    int instance;
    int m_simpleData;
    std::vector<double> m_data;
};

int MoveableClass::Instances = 0;
bool MoveableClass::Elided = true;

std::vector<double> BunchOfData() {
    return std::vector<double>(9999999);
}

int SimpleData() {
    return 9999999;
}

MoveableClass CreateRVO() {
    return MoveableClass(BunchOfData());
}

MoveableClass CreateNRVO() {
    MoveableClass named(BunchOfData());
    return named;
}

MoveableClass CreateRVO_Simple() {
    return MoveableClass(SimpleData());
}

MoveableClass CreateNRVO_Simple() {
    MoveableClass named(SimpleData());
    return named;
}

int main(int argc, char* argv[]) {
    std::cout << "\nMove assign from RVO: " << '\n';
    {
        MoveableClass a;
        a = CreateRVO();
    }
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from RVO simple: " << '\n';
    {
        MoveableClass a;
        a = CreateRVO_Simple();
    }
    std::cout << "Move elided: " <<  (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from NRVO: " << '\n';
    {
        MoveableClass a;
        a = CreateNRVO();
    }
    std::cout << "Move elided: " <<  (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test

    std::cout << "\nMove assign from NRVO simple: " << std::endl;
    {
        MoveableClass a;
        a = CreateNRVO_Simple();
    }
    std::cout << "Move elided: " << (MoveableClass::Elided ? "Yes" : "No") << '\n';
    MoveableClass::Elided = true;   // reset for next test
}

以下是在Visual C ++ 10.0(Beta 2)上以发布模式编译时得到的输出:

  

从RVO移动分配:
  构造实例1(无数据)
  构造实例2(带数据)
  从2的移动构造实例3   销毁实例2
  从3中分配给实例1   销毁实例3
  销毁实例1
  移动被忽略:否

     

从RVO移动分配简单:
  构造实例1(无数据)
  构造实例2(使用简单数据)
  从2中分配给实例1   销毁实例2
  销毁实例1
  移动已取消:是

     

从NRVO移动分配:
  构造实例1(无数据)
  构造实例2(带数据)
  从2中分配给实例1   销毁实例2
  销毁实例1
  移动已取消:是

     

简单地从NRVO移动分配:
  构造实例1(无数据)
  构造实例2(使用简单数据)
  从2中分配给实例1   销毁实例2
  销毁实例1
  移动已取消:是

然而,我对一件事情感到困惑。正如你所看到的,除了第一个动作之外,所有的动作都被省略了。为什么编译器不能在第86行使用MoveableClass(s​​td :: vector)执行RVO,但在第97行可以使用MoveableClass(int)?这只是MSVC的一个错误还是有充分的理由呢?如果有充分的理由,为什么它仍然可以在第91行的MoveableClass(s​​td :: vector)上执行NRVO?

我想理解它,所以我可以快乐地入睡。 :)

3 个答案:

答案 0 :(得分:2)

感谢您回复戴夫。

我已将测试添加到该示例中:
pastebin.com/f7c8ca0d6

奇怪的是,它表明除了NRVO之外,所有类型的精益都没有被执行! 编辑:实际上我认为这是因为它是对象有名的唯一测试。

我也尝试过其他STL类型并获得相同的结果。但是,在尝试我自己的非pod类型时,它按预期工作。我无法想象可能造成这种情况的STL类型有什么特别之处,所以我不知道还有什么可以尝试。

我会提交一份错误报告 修改:Submitted here

由于

答案 1 :(得分:1)

嗯。

似乎你改变了数据构造函数

MoveableClass::MoveableClass(std::vector<double> data)

通过引用接受向量,如此,

MoveableClass::MoveableClass(const std::vector<double>& data) 

它工作正常!如果按值传递向量,为什么它不起作用?

此外,这是一个应该在早期版本的MSVC上编译的版本,如果有人想在那里运行测试的话。它不包含C ++ 0x功能:http://pastebin.com/f3bcb6ed1

答案 2 :(得分:0)

在您的测试版本失败的情况下,从this example更新和维护cpp-next可能是个好主意,因此可以进行一次全面的规范测试。