简体   繁体   English

复制临时对象省略

[英]copy elision of temporary object

CPP Refs states: 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 —当尚未绑定到引用(12.2)的临时类对象将被复制/移动到具有相同cv-unqualtype类型的类对象时,可以通过将临时对象直接构造为以下形式来省略复制/移动操作:省略复制/移动的目标

Let's say I have some test code: 假设我有一些测试代码:

#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();
}

The output of the test is: 测试的输出为:

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

Elision external test

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

The function elisionExternal does work as expected, but i don't know why the elisionInternal is doing the copy operation, since the MyVec is temporary object? 函数elisionExternal确实可以按预期工作,但是我不知道为什么elisionInternal在执行复制操作,因为MyVec是临时对象?

Several things are going on here. 这里发生了几件事。

  • funcElision is omitting the copy in its return because of Named Return Value Optimization (NRVO), a special rule for named return values when: "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." funcElision由于命名返回值优化(NRVO)(在以下情况下对命名返回值的特殊规则:“函数按值返回类类型,并且返回语句的表达式是非易失性的名称”,因此省略了返回值中的副本)。具有自动存储持续时间的对象,该对象不是函数参数或catch子句参数,并且具有与函数的返回类型相同的类型(忽略顶级cv限定)。”
  • In funcElisionExternal , the return value of funcElision is used as a "nameless temporary", which can specifically elide copies (and moves post-C++11). funcElisionExternal ,的返回值funcElision被用作“无名临时”,其可以特异性的Elid副本(和移动后的C ++ 11)。 An A is then constructed and returned using Return Value Optimization (RVO) since it is also a nameless temporary. 然后使用返回值优化(RVO)构造A并将其返回,因为它也是一个无名的临时值。
  • In funcElisionInternal , tmp is a named temporary, so it can only elide copies with NRVO. funcElisionInternaltmp是一个命名的临时文件,因此它只能使用NRVO取消副本。 However, it is not the value that is being returned, and does not have the same type as the function's return signature - it is passed to A's constructor first. 但是,它不是要返回的值,并且与函数的返回签名的类型不同-首先将其传递给A的构造函数。 So it cannot use NRVO. 因此它不能使用NRVO。

Other rules for copy elision have to do with throw expressions and exceptions, so are not applicable here. 复制省略的其他规则与throw表达式和异常有关,因此此处不适用。

See cppreference's page on copy elision for more. 有关更多信息,请参见cppreference的页面

 return A(tmp); 

A 's constructor that takes a MyVector takes it by value. A的构造函数,一个MyVector按值需要它。 Therefore, A 's constructor parameter is initialized by copying from the glvalue. 因此, A的构造函数参数是通过从glvalue复制来初始化的。 There is no possibility of elision here because tmp is not a prvalue; 这里没有省略的可能,因为tmp不是prvalue。 it is an lvalue. 它是一个左值。

You can only elide from an lvalue if it is being returned directly . 如果直接返回左值,则只能从左值中跳过。 That is, if you returned tmp , and the return type was decltype(tmp) , then elision could take place (as you do with funcElision ). 也就是说,如果返回tmp ,并且返回类型为decltype(tmp) ,则可以进行funcElision (就像对funcElision )。 But otherwise, no. 但除此之外,没有。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM