C++ 这是C+的正确用法吗+';移动';语义学?

C++ 这是C+的正确用法吗+';移动';语义学?,c++,c++11,move-semantics,c++-standard-library,C++,C++11,Move Semantics,C++ Standard Library,今晚,我看了过去几天一直在编写的一些代码,并开始阅读移动语义,特别是std::move。我有几个问题要问专业人士,以确保我走的是正确的道路,而不是做出任何愚蠢的假设 首先: 1)最初,我的代码有一个返回大向量的函数: template<class T> class MyObject { public: std::vector<T> doSomething() const; { std::vector<T> theVector;

今晚,我看了过去几天一直在编写的一些代码,并开始阅读移动语义,特别是std::move。我有几个问题要问专业人士,以确保我走的是正确的道路,而不是做出任何愚蠢的假设

首先:

1)最初,我的代码有一个返回大向量的函数:

template<class T> class MyObject
{
public:
    std::vector<T> doSomething() const;
    {
        std::vector<T> theVector;

        // produce/work with a vector right here

        return(theVector);
    }; // eo doSomething
};  // eo class MyObject
3)最后,我想做一些性能测试,是因为std::move semantics我得到了惊人的快速结果,还是我的编译器(VS2010)也做了一些优化?

(为了简洁起见,省略了
\u getmillizes()
的实现)

std::vector v;
对于(整数a(0);a<1000000;++a)
v、 推回(a);
std::向量x;
对于(整数a(0);a<1000000;++a)
x、 推回(a);
int s1=_getmillizes();
标准::向量v2=v;
int s2=_getmillizes();
std::vector v3=std::move(x);
int s3=_getmillizes();
int result1=s2-s1;
int result2=s3-s2;
很明显,结果非常棒。结果1为标准作业,耗时630ms。第二个结果是0毫秒。这是对这些东西的良好性能测试吗

我知道其中一些对你们很多人来说是显而易见的,但我想在我开始编写代码之前确保我理解了语义


提前谢谢

移动某物的标准方法是使用
std::move(x)
,而不是
静态施法。顺便说一句,命名的返回值优化很可能是通过按值返回向量来实现的,因此它在移动语义之前也会表现得很好

您的性能测试很好地说明了移动语义如何有利于性能:复制赋值必须复制一百万个元素,而移动赋值基本上只是交换向量的内部指针,这是一个简单的单词赋值或两个,只是几个周期

由于特殊的语言规则,这已经隐式移动,因为向量是本地对象。见第12.8节第34和35段:

当满足某些条件时,允许实现省略类的复制/移动构造 对象,即使对象的复制/移动构造函数和/或析构函数有副作用。在这种情况下, 该实现将省略的复制/移动操作的源和目标视为两个不同的操作 指代同一对象的方式,并且该对象的销毁发生在时间较晚的时候 如果没有优化,两个对象将被破坏。这种对复制/移动的省略 在以下情况下允许进行称为复制省略的操作 消除多个副本):

-在具有类返回类型的函数的return语句中,当表达式是 非易失性自动对象,其cv类型与函数返回类型相同 复制/移动操作可以通过将自动对象直接构造到函数的 返回值

[……]

当满足省略复制操作的条件且要复制的对象由 首先执行左值、重载解析以选择副本的构造函数,就像对象是 由右值指定

请注意,必须返回
std::vector
(按值),而不是
std::vector&&
(按引用)

但是为什么要加括号呢<代码>返回
不是一个函数:


引用仍然是引用。同样,在C++03中不能返回对本地的引用(或者获得UB),在C++0x中也不能。您最终将引用一个死对象;它恰好是一个右值引用。所以这是错误的:

std::vector<T>&& doSomething() const
{
    std::vector<T> local;

    return local; // oops
    return std::move(local); // also oops
}
std::vector&&doSomething()常量
{
std::向量局部;
返回本地;//oops
返回std::move(local);//也返回oops
}
你应该做你在第二条中看到的事情:

// okay, return by-value 
std::vector<T> doSomething() const
{
    std::vector<T> local;

    return local; // exactly the same as:
    return std::move(local); // move-construct value
}
//好的,按值返回
std::vector doSomething()常量
{
std::向量局部;
return local;//与以下内容完全相同:
返回std::move(local);//move构造值
}
函数的局部变量在返回时是临时的,因此无需更改任何代码。返回类型是负责实现移动语义的对象,而不是您

您希望使用
std::move
来显式移动某些内容,而在测试中通常不会这样做。(这似乎很好;这是发行版吗?您应该输出向量的内容,否则编译器将对其进行优化。)


如果您想了解右值引用,.

要添加到GMan的答案中:即使您将返回类型更改为
std::vector
(没有任何引用,否则您将得到UB),您在“1”中更改返回表达式也不会使性能更好,但可能会使性能更差。由于
std::vector
具有移动构造函数,并且您返回一个本地对象,因此无论您编写
返回向量,都不会调用
vector
的复制构造函数
返回静态广播(矢量)
,或
返回std::move(theVector)
。在最后两种情况下,编译器将被迫调用move构造函数。但在第一种情况下,如果它可以为该功能执行NRVO,它可以自由地优化整个移动。如果NRVO由于某种原因不可能,那么编译器将求助于调用move构造函数。所以不要改变
返回x
返回std::move(x)
如果
x
是从中返回的函数中的本地非静态对象,否则将阻止编译器使用另一个优化机会。

为什么要使用
static\u cast
而不是
std::vector<int> v;
for(int a(0); a < 1000000; ++a)
    v.push_back(a);

std::vector<int> x;
for(int a(0); a < 1000000; ++a)
    x.push_back(a);

    int s1 = _getMilliseconds();
std::vector<int> v2 = v;
    int s2 =  _getMilliseconds();
std::vector<int> v3 = std::move(x);
    int s3 =  _getMilliseconds();

    int result1 = s2 - s1;
    int result2 = s3 - s2;
return(theVector);
return theVector;
std::vector<T>&& doSomething() const
{
    std::vector<T> local;

    return local; // oops
    return std::move(local); // also oops
}
// okay, return by-value 
std::vector<T> doSomething() const
{
    std::vector<T> local;

    return local; // exactly the same as:
    return std::move(local); // move-construct value
}