复制临时对象的省略

时间:2017-12-05 22:00:50

标签: c++ c++17 copy-elision

CPP Refs状态:

  

- 当一个未绑定到引用(12.2)的临时类对象被复制/移动到具有相同cv-nonqualified类型的类对象时,可以通过构造临时对象来省略复制/移动操作直接进入省略的复制/移动目标

假设我有一些测试代码:

#include <iostream>
#include <vector>

using namespace std;

template <typename T>
class MyVector : public vector<T>
{ 
public:
    MyVector()
    {
        cout << "MyVector()" << endl;
    }

    MyVector(const MyVector& right):
        vector<T>(right)
    {
        cout << "MyVector(const MV&)" << endl;
    }

    MyVector(MyVector&& right) :
        vector<T>(right)
    {
        cout << "MyVector(MV&&)" << endl;
    }

};

class A
{
public:
    A() = default;
    A(MyVector<char> vec) :
        _vec(std::move(vec))
    {
        cout << "A(MyVec)" << endl;
    }
private:
    MyVector<char> _vec;
};

MyVector<char> funcElision()
{
    cout << "\nElision" << endl;
    MyVector<char> tmp;
    tmp.emplace_back('a');
    return tmp;
}

A funcElisionExternal()
{
    cout << "\nElision external test" << endl;
    return A(funcElision());
}


A funcElisionInternal()
{
    cout << "Elision internal test" << endl;
    MyVector<char> tmp;
    tmp.emplace_back('a');
    return A(tmp);
}

int main()
{
    auto a = funcElisionInternal();
    auto b = funcElisionExternal();
}

测试的输出是:

Elision internal test
MyVector()
MyVector(const MV&)
MyVector(MV&&)
A(MyVec)

Elision external test

Elision
MyVector()
MyVector(MV&&)
A(MyVec)
 End

函数elisionExternal按预期工作,但我不知道为什么elisionInternal正在进行复制操作,因为MyVec是临时对象?

2 个答案:

答案 0 :(得分:2)

这里发生了一些事情。

  • funcElision由于命名返回值优化(NRVO)而在其返回中省略了副本,这是一个特殊规则,用于命名返回值:“函数按值返回类型,返回语句的表达式为具有自动存储持续时间的非易失性对象的名称,该对象不是函数参数或catch子句参数,并且具有与函数的返回类型相同的类型(忽略顶级cv限定)。 “
  • funcElisionExternal中,funcElision的返回值用作“无名临时”,可以专门删除副本(并移动后C ++ 11)。然后使用返回值优化(RVO)构造并返回A,因为它也是无名的临时值。
  • funcElisionInternal中,tmp是一个名为临时的,因此只能使用NRVO删除副本。但是,它不是返回的值,并且与函数的返回签名没有相同的类型 - 它首先传递给A的构造函数。所以它不能使用NRVO。

复制省略的其他规则与throw表达式和异常有关,所以这里不适用。

有关详情,请参阅cppreference's page on copy elision

答案 1 :(得分:0)

return A(tmp);

A使用MyVector的构造函数按值获取它。因此,通过从glvalue复制来初始化A的构造函数参数。这里没有可能省略,因为tmp不是prvalue;这是一个左值。

如果直接返回,您只能从左值中删除。也就是说,如果您返回tmp,并且返回类型为decltype(tmp),则可以进行省略(与funcElision一样)。但除此之外,没有。