2

我们有一个广泛使用参数的代码库,因为每个函数都可能因一些错误枚举而失败。这变得非常混乱,代码有时不可读。

我想消除这种模式并带来更现代的方法。

目标是改造:

error_t fn(param_t *out) {  
    //filling 'out'
}
param_t param;
error_t err = fn(&param);

变成类似的东西:

std::expected<error_t, param_t> fn() {
    param_t ret;
    //filling 'ret'
    return ret;
}
auto& [err, param] = fn();

以下问题是为了让我自己和其他人相信这种改变是最好的:

  1. 我知道在标准级别上,NRVO 不是强制性的(与 c++17 中的 RVO 不同),但实际上有没有可能在任何主要编译器中都不会发生?
  2. 使用 out 参数代替 NRVO 有什么好处吗?
  3. 假设 NRVO 发生,生成的程序集是否有显着变化(假设一个优化的expected实现 [也许用布尔值表示是否发生错误完全消失])?
4

1 回答 1

1

首先,一些假设:

  1. 我们正在查看未内联的函数。在这种情况下,它几乎可以保证是绝对等价的。

  2. 我们将假设函数的调用站点在使用返回值之前实际检查错误情况。

  3. 我们将假设返回的值尚未使用部分数据进行预初始化。

  4. 我们将假设我们只关心这里的优化代码。

正在建立:

我知道在标准级别上,NRVO 不是强制性的(与 c++17 中的 RVO 不同),但实际上有没有可能在任何主要编译器中都不会发生?

假设此时正在执行 NRVO 是一个安全的赌注。我确信有人会想出一个不会发生的人为情况,但我通常相信在几乎所有用例中,NRVO 都在当前的现代编译器上执行。

话虽如此,我永远不会依赖这种行为来保证程序的正确性。IE 我不会假设它不会因为 NRVO 而被调用,所以我不会制作一个带有副作用的奇怪的复制构造函数。

使用 out 参数代替 NRVO 有什么好处吗?

一般来说不会,但就像 C++ 中的所有东西一样,它可能会出现在极端情况下。用于最大化缓存一致性的显式内存布局将是“通过指针返回”的一个很好的用例。

假设 NRVO 发生,生成的程序集是否有显着变化(假设优化的预期实现 [也许用布尔值表示是否发生错误完全消失])?

这个问题对我来说没有多大意义。expected<>的行为更像 avariant<>而不是 a tuple<>,因此“表示错误是否完全消失的布尔值”并没有真正的意义。

话虽如此,我认为我们可以使用 std::variant 来估计:

https://godbolt.org/g/XpqLLG

它是“不同的”,但在我看来不一定更好或更坏。

于 2018-05-27T22:00:13.397 回答