c++ 为什么NRVO不适用于结构化绑定?

okxuctiv  于 2023-10-20  发布在  其他
关注(0)|答案(1)|浏览(110)

Y部分。
考虑这些examples

#include <iostream>

struct movable
{
    movable() {
        std::cout << "movable()\n";
    }

    movable(movable&&) noexcept {
        std::cout << "movable(&&)\n";
    }

    movable(const movable&) noexcept {
        std::cout << "movable(const &)\n";
    }

    ~movable() {
        std::cout << "~movable()\n";
    }
};

movable rvo()
{
    return {};
}

movable nrvo()
{
    movable m;
    return m;
}

movable cnrvo()
{
    const movable m;
    return m;
}

movable binding_nrvo()
{
    struct binding { movable m; };
    auto [m] = binding{};
    // explicit move is required
    // return std::move(m);

    // otherwise a copy would be made
    return m;
}

int main()
{
    {
        std::cout << "rvo:\n";
        movable m = rvo();
    }

    {
        std::cout << "\nnrvo:\n";
        movable m = nrvo();
    }

    {
        std::cout << "\ncnrvo:\n";
        movable m = cnrvo();
    }

    {
        std::cout << "\nbinding_nrvo:\n";
        movable m = binding_nrvo();
    }

    return 0;
}

-std=c++17 -O3的输出如下:

rvo:
movable()
~movable()

nrvo:
movable()
~movable()

cnrvo:
movable()
~movable()

binding_nrvo:
movable()
movable(const &)
~movable()
~movable()

我知道NRVO不适用于“wrapped”值,例如你必须显式地使用std::move,如return std::move(std::get<0>(tuple));,以防止复制。
但在这里,绑定是“按值”发生的:auto [m] = binding;。因此,我认为m是一个独立的对象。为什么NRVO不发生在这里?

X部分

问题不是关于“X”,而是关于上下文:
我正在尝试使用结构化绑定,以便将它们用于“类似go”的控制流来返回错误。最理想的方法是拥有这样的东西:

const auto [value, err] = getSome();
if (err)
  return err;

在我看来,如果没有类似预期的返回类型,这似乎是最干净的控制流,而不需要求助于异常。我期望这在const err上执行NRVO(就像我的cnrvo()示例中一样),但它没有发生。
因为你必须使用std::move,所以也没有办法将value作为const。
最后它变成了

auto [value, errGetSome] = getSome(); // can't have several `err` in the scope
if (!errGetSome)
  return std::move(errGetSome);

// just promise not to modify `value`
const auto& actualValue = value;

……这就不重要了。
不管怎样,我想知道为什么。

wljmcqd8

wljmcqd81#

结构化绑定不会创建N个单独的对象。它创建了一个未命名的变量,加上N个(有点神奇)对其成员的引用。
当调用者为NRVO分配接收对象时,不能期望分配过多的空间来适应结构化绑定中的整个未命名变量,在一般情况下这可能是不可能的。
对于像您这样的简单的单元素结构化绑定来说,没有什么特别的原因是不可能的,我猜没有人费心去优化这种特殊情况。
关于X部分,我没有看到一个好的解决方案。如果要返回整个对,就不要使用结构化绑定,然后可以NRVO该对本身。

相关问题