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

Ser*_*nik 0 c++ c++17 structured-bindings

Y部分。

考虑这些例子

#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;
}
Run Code Online (Sandbox Code Playgroud)

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

rvo:
movable()
~movable()

nrvo:
movable()
~movable()

cnrvo:
movable()
~movable()

binding_nrvo:
movable()
movable(const &)
~movable()
~movable()
Run Code Online (Sandbox Code Playgroud)

我知道 NRVO 不适用于“包装”值,例如您必须显式使用std::movelikereturn std::move(std::get<0>(tuple));才能防止复制。

但这里的绑定是“按值”发生的auto [m] = binding;:因此,我相信这m是一个独立的对象。为什么 NRVO 没有在这里发生?

X部分

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

const auto [value, err] = getSome();
if (err)
  return err;
Run Code Online (Sandbox Code Playgroud)

如果没有类似预期的返回类型,在我看来,这是最干净的控制流,无需诉诸异常。我预计这会在 const 上执行 NRVO err(就像我的示例中的cnrvo()),但它没有发生。
因为你必须使用std::move,所以也没有办法使用valueas 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;
Run Code Online (Sandbox Code Playgroud)

......这打败了整个要点。无论如何,我想知道为什么
部分 的答案。

Hol*_*Cat 6

结构化绑定不会创建 N 个单独的对象。它创建一个未命名的变量,以及对其成员的 N 个(有点神奇的)引用。

当调用者为 NRVO 分配接收对象时,不能期望分配过多的空间来容纳结构化绑定中的整个未命名变量,并且在一般情况下这可能是不可能的。

没有什么特别的原因为什么像你这样的简单的单元素结构化绑定是不可能的,我想没有人费心去优化这个特殊情况。


关于X部分,我没有看到好的解决方案。如果您要返回整个对,请不要使用结构化绑定,然后您可以 NRVO 该对本身。

  • @SergeyKolesnik *““简单的单元素”只是一个代码示例。可能有多达“*是的。正如我所说,虽然理论上可以支持单元素绑定,但多元素绑定是不可能的。*“返回一对”没有问题。只返回一个就有问题”*同样,正如我所说,在一般情况下我没有看到一个好的解决方案。*“标准是否保证必须创建这样的对象?”* 是的,请参阅http://eel.is/c++draft/dcl.struct.bind#1 (3认同)