【问题标题】:copy elision of temporary object临时对象的复制省略
【发布时间】:2017-12-05 22:00:50
【问题描述】:

CPP Refs 指出:

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

假设我有一些测试代码:

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

【问题讨论】:

  • 临时A 对象是用tmp 的副本构造的,因为那是您编写的代码。具体是哪一部分不清楚?
  • tmp 不是临时对象。

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


【解决方案1】:

这里发生了一些事情。

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

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

请参阅cppreference's page on copy elision 了解更多信息。

【讨论】:

  • 那么可以用A制作tmp elide copy吗?如果有,怎么做?
【解决方案2】:
return A(tmp);

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

只有在直接返回左值时,才能从左值中省略。也就是说,如果您返回tmp,并且返回类型为decltype(tmp),则可能会发生省略(就像您对funcElision 所做的那样)。但除此之外,没有。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-05-14
    • 2019-05-12
    • 1970-01-01
    • 2015-07-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多