事先道歉,我知道评估顺序的一般主题已经有很多SO问题。但是,在研究了它们之后,我想澄清一些具体的观点,我认为这些观点并不构成任何重复。假设我有以下代码:

#include <iostream>

auto myLambda(int& n)
{
    ++n;
    return [](int param) { std::cout << "param: " << param << std::endl; };
}

int main()
{
    int n{0};

    myLambda(n)(n);
    return 0;
}

上面的程序在编译时输出“n:0”。在这里,我们有未指定的顺序在起作用:如果执行了不同的评估顺序,它可能很容易输出“n:1”。

我的问题是:
  • 在上述最后一个函数调用(即lambda表达式调用)过程中,后缀表达式myLambda(0),其参数n和后续函数调用本身之间的顺序关系到底是什么?
  • 上面是未定义或未指定行为的示例吗?为什么要这么做(引用标准)?
  • 如果我将lambda代码更改为[](int param) { std::cout << "hello" << std::endl }(即使结果独立于其参数,从而使任何评估顺序决定,从而确定行为),上述2)的答案是否仍将相同?

  • 编辑:我已将lambda参数名称从'n'更改为'param',因为这似乎引起了困惑。

    最佳答案

    具有讽刺意味的是(由于该示例使用了C++ 11功能,并且使其他答案分散了注意力),使该示例具有未指定行为的逻辑可以追溯到C++ 98,第5节,第4段



    本质上,所有C++标准中都存在相同的子句,尽管正如Marc van Leeuwen的评论所指出的那样,最近的C++标准不再使用序列点的概念。最终结果是相同的:在一条语句中,运算符的操作数的顺序或求值以及单个表达式的子表达式仍未指定。

    发生未指定的行为是因为表达式n在语句中被评估了两次

    myLambda(n)(n);
    

    表达式n的一个评估(以获得引用)与第一(n)相关联,表达式n的另一评估(获取值)与第二(n)相关联。未指定这两个表达式的评估顺序(即使从表面上看,它们都是n)。

    所有C++标准中都存在类似的子句,并且结果相同-不管myLambda(n)(n)如何实现,语句myLambda()的未指定行为

    例如,可以在C++ 98(以及所有更高版本的C++标准,包括C++ 11和更高版本)中实现myLambda()
     class functor
     {
          functor() {};
          int operator()(int n) { std::cout << "n: " << n << std::endl; };
     };
    
     functor myLambda(int &n)
     {
           ++n;
           return functor();
     }
    
     int main()
     {
          int n = 0;
    
          myLambda(n)(n);
          return 0;
     }
    

    因为问题中的代码只是一种(C++ 11)技术(或简称),用于实现与此相同的效果。

    上面的答案回答了OP的问题1.和2.。未指定的行为发生在main()中,与myLambda()本身的实现方式无关。

    为了回答OP的第三个问题,如果lambda(或本例中的仿函数的operator())被修改为不访问其参数的值,则该行为仍未指定。唯一的区别是该程序作为一个整体不会产生可见的输出,而这些输出在编译器之间可能会有所不同。

    关于c++ - C++后缀表达式未定义与未指定行为,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/37344370/

    10-13 03:00