23

假设我们有一个名为AAA同时支持复制/移动的类:

class AAA
{
public:
    AAA() = default;
    ~AAA() = default;

    AAA(const AAA& rhs)
    {
       std::cout << "Copy constructor" << std::endl;
    }

    AAA(AAA&& rhs)
    {
       std::cout << "Move constructor" << std::endl;
    }
};

在以下代码中,get_val返回second

AAA get_val()
{
    auto [ first, second ]  = std::make_tuple(AAA{}, AAA{});

    std::cout << "Returning - " << std::endl;
    return second;
}

auto obj = get_val();
std::cout << "Returned - " << std::endl;

现在second被复制,打印以下输出:

...
Returning - 
Copy constructor 
Returned -

这是不幸的,因为我对结果的期望是没有调用复制构造函数,或者至少它被隐式移动了。

为了避免复制,我必须明确地应用std::move它。

return std::move(second);

然后我会收到以下结果:

...
Returning - 
Move constructor 
Returned - 

我假设未执行RVO的原因是编译器可能会将second其视为参考,而get_val返回纯右值。

但是,为什么也不能期望隐式移动呢?在这种特殊情况下,在 return 语句上使用显式std::move看起来并不直观,因为您通常不希望 RVO(在大多数情况下是比 move 更好的优化)意外消失

由编译器 gcc 和 clang 测试-O3

现场演示

4

1 回答 1

19

但是,为什么也不能期望隐式移动呢?

与关闭省略的原因完全相同:因为它是一个引用,而不是独立对象的名称。每次使用second本质上都等同于说obj.whateverget<1>(obj)(尽管在后一种情况下,我们存储引用)。这些表达式中的任何一个都没有隐含的举动。

结构化绑定用于访问给定对象的子对象。您不能省略子对象的返回,也不能隐式地从它们移开。因此,您不能省略结构化绑定名称,也不能隐式移出它们。

于 2018-04-01T14:15:53.900 回答