【问题标题】:C++ forcing function parameter evaluation orderC++ 强制函数参数求值顺序
【发布时间】:2019-10-10 21:26:36
【问题描述】:

我明白当我调用诸如

之类的函数时
a(b(),c());

那么这个行为可能在 = C++17 中是未指定的,从某种意义上说,由编译器决定是评估 b 还是 @987654324先@@。

我想知道强制评估订单的最佳方式。我将编译为 C++14。

马上想到的是这样的:

#include <iostream>

int count = 5;
auto increment(){
    return count++;
}

template <typename A, typename B>
auto diff(A && a, B && b){
   return a - b;
}

int main() {
    auto && a = increment();
    auto && b = increment();
    auto c = diff(a,b);
}

我是否处于未定义的行为领域?或者这就是“应该”强制评估顺序的方式?

【问题讨论】:

  • 你认为为什么会有ub?
  • @Peter 即使两个函数使用(读取和/或写入)全局变量,行为也不会未定义,而只是未指定。
  • @Peter Expressions 可以以交错的方式计算,直到 C++14,但即使这样,参数中的每个函数调用也必须作为一个整体执行。 从来没有函数可以交错或并行执行。请参阅Rules section 中的规则 11(以及更下方的序列点规则中的规则 4)。
  • @bremen_matt:diff 没有区别(双关语),它仍然只是未指定,而不是 UB。在这方面,标准版本之间没有区别,这始终是未指定的。
  • @bremen_matt:未定义意味着任何事情都可能发生(标准没有指定应该发生什么)。未指定意味着b() 然后c(),或c() 然后b()。其中之一会发生。它没有指定,哪个。

标签: c++ c++14


【解决方案1】:

分隔语句的分号强加了“发生在之前”的关系。 auto &amp;&amp; a = increment() 必须首先评估。这是有保证的。返回的临时值将在第二次调用 increment 之前绑定到引用 a(并延长其生命周期)。

没有UB。这是强制评估顺序的方式。

这里唯一的问题是如果increment 本身返回了一个引用,那么您需要担心生命周期问题。但是,如果没有生命周期问题,比如它返回了对 count 的引用,那么对 ab 的强制评估仍然不会出现 UB。

【讨论】:

    【解决方案2】:

    这是强制评估顺序的另一种方法,使用 std::initializer_list,它保证从左到右的评估顺序:

    #include <numeric> // for accumulate
    #include <initializer_list>
    
    template <class T>
    auto diff(std::initializer_list<T> args)
    {
       return std::accumulate(args.begin(), args.end(), T(0), std::minus<>{});
    }
    
    const auto result = diff({increment(), increment()});
    

    这将您限制为相同类型的对象,并且您需要键入额外的大括号。

    【讨论】:

    • 不确定,但我认为您可以通过元组使其与不同类型一起使用:std::apply(func, std::tuple&lt;funcsargs&gt;({__VA_ARGS__}));
    • 应用我认为是 C++17
    • @bremen_matt 感谢您为与问题保持一致而进行的编辑。 Minor nitpick:std::accumulate 默认执行 std::plus&lt;&gt;,因此我们需要传递一个 std::minus&lt;&gt; 实例来执行实际减法。
    • 糟糕。从裂缝中溜走。我稍微更改了示例,以便其他人更容易理解为什么执行顺序很重要。在前面的示例中,它实际上并不重要。
    猜你喜欢
    • 2011-02-25
    • 1970-01-01
    • 2022-11-14
    相关资源
    最近更新 更多