存在错误代码时的输出参数与 NRVO

Out params vs. NRVO in the presence of error codes

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

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

目标是改造:

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 实现 [可能表示错误发生的布尔值完全消失])?

首先,一些假设:

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

  2. 我们假设函数的调用点在使用返回值之前实际检查了错误条件。

  3. 我们假设返回值没有用部分数据预初始化。

  4. 我们假设我们只关心优化代码。

正在成立:

I know that on the standard level, NRVO is not mandatory (unlike RVO in c++17) but practically is there any chance it won't happen in any of the major compilers?

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

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

Are there any advantages of using out parameters instead of NRVO?

通常不会,但就像 C++ 中的所有内容一样,在某些边缘情况下可能会出现。用于最大化缓存一致性的显式内存布局将是 "returning by pointer".

的一个很好的用例

Assuming NRVO happens, is there a a significant change in the generated assembly (assuming an optimized expected implementation [perhaps with the boolean representing whether an error occured completly disappear])?

这个问题对我来说意义不大。 expected<>tuple<> 更像 variant<>,所以 "boolean representing whether an error occured completly disappear" 没有意义。

话虽这么说,我想我们可以用std::variant来估计:

https://godbolt.org/g/XpqLLG

它是 "different",但在我看来不一定更好或更坏。