临时对象的复制省略

copy elision of temporary object

CPP 参考指出:

— when a temporary class object that has not been bound to a reference (12.2) would be copied/moved to a class object with the same cv-unqualified type, the copy/move operation can be omitted by constructing the temporary object directly into the target of the omitted copy/move

假设我有一些测试代码:

#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 是临时对象?

这里发生了几件事。

  • funcElision 正在省略其 return 中的副本,因为命名 Return 值优化 (NRVO),命名 return 值的特殊规则,当:"a function returns a class type by value, and the return statement's expression is the name of a non-volatile object with automatic storage duration, which isn't a function parameter, or a catch clause parameter, and which has the same type (ignoring top-level cv-qualification) as the return type of the function."
  • funcElisionExternal中,funcElision的return值作为一个"nameless temporary",可以专门省略副本(并移动post-C ++11)。然后构造 A 并使用 Return 值优化 (RVO) returned,因为它也是一个无名的临时文件。
  • funcElisionInternal中,tmp是一个命名的临时文件,所以它只能用NRVO删除副本。但是,它不是被 returned 的值,并且与函数的 return 签名不具有相同的类型 - 它首先传递给 A 的构造函数。所以它不能使用NRVO。

复制省略的其他规则与抛出表达式和异常有关,因此不适用于此处。

有关更多信息,请参阅 cppreference's page on copy elision

return A(tmp);

A 的构造函数接受了 MyVector 的值。因此,A的构造函数参数是通过从glvalue复制来初始化的。这里不可能省略,因为 tmp 不是纯右值;它是一个左值。

如果左值被直接return编辑,则只能从左值中删除。也就是说,如果您 return 编辑了 tmp,并且 return 类型是 decltype(tmp),则可以进行省略(就像您对 funcElision 所做的那样)。但除此之外,没有。