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