Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/162.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 存在错误代码时的Out参数与NRVO_C++_Optimization_Error Handling_Nrvo - Fatal编程技术网

C++ 存在错误代码时的Out参数与NRVO

C++ 存在错误代码时的Out参数与NRVO,c++,optimization,error-handling,nrvo,C++,Optimization,Error Handling,Nrvo,我们有一个广泛使用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;

我们有一个广泛使用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();
std::预期的fn(){
参数ret;
//填充“ret”
返回ret;
}
自动&[err,param]=fn();
以下问题是为了说服我自己和其他人这一改变是最好的:

  • 我知道在标准级别上,NRVO不是强制性的(不像c++17中的RVO),但实际上有没有可能在任何主要编译器中都不会发生这种情况
  • 使用out参数代替NRVO有什么好处吗
  • 假设发生了NRVO,生成的程序集中是否有重大变化(假设优化的
    预期的
    实现[可能使用表示错误是否完全消失的布尔值]

  • 首先,有几个假设:

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

  • 我们将假设函数的调用站点在使用返回值之前实际检查错误条件

  • 我们将假设返回值未使用部分数据进行预初始化

  • 我们假设这里只关心优化的代码

  • 正在设立的:

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

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

    也就是说,我永远不会依靠这种行为来保证程序的正确性。也就是说,我不会假设由于NRVO没有被调用而创建一个有副作用的奇怪的复制构造函数

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

    一般不,但是像C++中的所有东西一样,有边缘情况可以出现。最大化缓存一致性的显式内存布局对于“指针返回”来说是一个很好的用例

    假设发生了NRVO,生成的程序集中是否有重大更改(假设优化的预期实现[可能使用表示错误是否完全消失的布尔值])

    这个问题对我来说没有多大意义<代码>预期值
    的行为更像是一个
    变量
    而不是一个
    元组
    ,因此“表示错误是否完全消失的布尔值”实际上没有意义

    也就是说,我认为我们可以使用std::variant来估计:


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

    假设您只询问非内联场景是否安全?因为一旦内联开始,它基本上是100%等价的。@Frank它关心的是是否根据内联的假设来构造代码。因此,“当内联不能发生时,我可以依赖它吗”(比如说有一天我切换编译器,我的代码会变慢吗?)'std::expected代表了一个实质性的api更改(为了更好),这使得重构有点不等价。切换到std::tuple将是一个更简单的1对1匹配。具体来说,将性能与调用忽略错误的代码进行比较是不公平的。@Frank我没有列出使用“expected”的所有优点,因为它实际上与问题无关。事实上,其中一个考虑因素是不允许忽略错误(通过nodiscard警告)。这与您关心比较这两种方法以及比较生成的程序集有关。但是,比较仅对使用错误的代码公平,并且只传递默认初始化的
    param\t
    。所以我只想在得出答案之前确定这些假设是有效的。谢谢你的回答,我有几个问题:首先,为什么你的假设3是必要的?如果我们在这两种情况下使用相同的部分初始化,我看不出有任何区别。其次,对于
    variant
    注释,我认为我们通常只在调用函数后立即测试是否有错误,因此可以优化
    variant
    的“标记”。@Omerosler假设#3是必要的,因为否则,可以多次重用部分烘焙的结果,只付一次费用。就变量而言,假设1主要涵盖了这一点。由于生成函数的代码不知道结果将如何使用,因此它别无选择,只能生成标记并将其存储在结果中。但是,(在
    预期的情况下,
    ),除非您的错误类型设法小于
    bool
    (祝您好运),否则您将领先,因为错误和有效负载之间的存储重叠。我明白您对#3的意思,事实上这就是用例。至于标记,如果您永远无法在err5or和return之间切换(标记是
    const
    ),那么它就不同于完整的变量,因此我认为编译器可以完全省略这个变量(因为它可以看到它是如何完整地使用的)