【问题标题】:Declaring and initializing a variable in a Conditional or Control statement in C++在 C++ 中的条件或控制语句中声明和初始化变量
【发布时间】:2010-12-03 18:24:55
【问题描述】:

在 Stroustrup 的C++ 编程语言:特别版(第 3 版)中,Stroustrup 写道,不仅允许,而且鼓励在控制语句的条件中声明和初始化变量。他写道,他鼓励这样做,因为它将变量的范围缩小到只有它们需要的范围。所以像这样......

if ((int i = read(socket)) < 0) {
    // handle error
}
else if (i > 0) {
    // handle input
}
else {
    return true;
}

...是很好的编程风格和实践。变量i 仅存在于需要它然后超出范围的if 语句块。

但是,g++(Ubuntu 4.3.3 版特定编译)似乎不支持编程语言的这个特性,这让我很惊讶。也许我只是用一个关闭它的标志调用 g++(我调用的标志是-g-Wall)。使用这些标志编译时,我的 g++ 版本返回以下编译错误:

socket.cpp:130: error: expected primary-expression before ‘int’
socket.cpp:130: error: expected `)' before ‘int’

在进一步的研究中,我发现我似乎并不是唯一一个使用不支持此功能的编译器的人。在this question 中似乎存在一些混淆,即究竟什么语法在该语言中应该是标准的,以及编译器用它编译什么。

所以问题是,哪些编译器支持此功能,需要设置哪些标志才能编译?是否符合某些标准而不符合其他标准?

另外,出于好奇,人们是否普遍同意 Stroustrup 认为这是一种好的风格?或者这是一种语言的创造者在他的脑海中产生了一个不一定得到该语言社区支持的想法的情况?

【问题讨论】:

  • 我不知道哪个编译器支持它,但我个人每次做任何不需要该变量的事情时都会使用这种方法,除了将其用作临时变量。不过同样的问题,这是不好的做法吗?
  • 我认为按块确定变量范围的整个想法是不好的做法,因为它可以原谅长方法。我推荐 Bob Martin 在他非常有用的著作Clean Code 中关于函数长度的激进主张;具体来说,一个方法实际上不应该有多个控制结构。

标签: c++ compiler-construction conditional declaration


【解决方案1】:

允许在嵌套块的控制部分声明变量,但在ifwhile的情况下,变量必须初始化为将被解释为条件的数字或布尔值.它不能包含在更复杂的表达式中!

在您展示的特定情况下,不幸的是,您似乎无法找到遵守的方法。

我个人认为让局部变量尽可能接近它们在代码中的实际生命周期是一种很好的做法,即使当您从 C 切换到 C++ 或从 Pascal 切换到 C++ 时这听起来令人震惊——我们习惯于看到所有变量在一处。有了一些习惯,您会发现它更具可读性,并且您不必在其他地方查找声明。此外,您知道在此之前它不会被使用。


编辑:

话虽如此,我认为在一个声明中混合过多并不是一个好习惯,我认为这是一个共同的观点。如果你影响一个变量的值,然后在另一个表达式中使用它,通过分开这两个部分,代码将更具可读性和更少的混乱。

所以不要使用这个:

int i;
if((i = read(socket)) < 0) {
    // handle error
}
else if(i > 0) {
    // handle input
}
else {
    return true;
}

我更喜欢这样:

int i = read(socket);
if(i < 0) {
    // handle error
}
else if(i > 0) {
    // handle input
}
else {
    return true;
}

【讨论】:

  • 第二个例子,不要使用“i”作为变量。对于严格的局部变量以外的任何内容,请使名称有意义。
【解决方案2】:

当与可能为 NULL 的指针一起使用时,我认为它是一种很好的风格:

if(CObj* p = GetOptionalValue()) {
   //Do something with p
}

这样无论p是否被声明,它都是一个有效的指针。没有悬空指针访问危险。

另一方面,至少在 VC++ 中,它是唯一支持的用途(即检查分配是否为真)

【讨论】:

    【解决方案3】:

    在这些情况下,我尽可能多地使用 const。而不是你的例子,我会这样做:

    const int readResult = read(socket);
    if(readResult < 0) {
        // handle error
    } 
    else if(readResult > 0)
    {
        // handle input
    } 
    else {
        return true;
    } 
    

    因此,虽然没有包含范围,但这并不重要,因为变量无法更改。

    【讨论】:

    • 我喜欢这样。 (现在有 15 个以上的字符)
    【解决方案4】:

    他们正在 c++17 中解决这个问题:

    if (int i = read(socket); i < 0)
    

    if 可以有一个初始化语句。

    http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0305r0.html

    【讨论】:

      【解决方案5】:

      我遇到过类似的problem

      问题似乎是int 声明周围的括号。如果您可以在没有它们的情况下表达作业和测试,它应该可以工作,即

      if (int i = read(socket)) {
      

      应该可以,但这意味着测试是!= 0,这不是你想要的。

      【讨论】:

        【解决方案6】:

        虽然您可以将声明用作布尔表达式,但不能将声明放在表达式的中间。我不禁认为您误读了 Bjarne 所说的内容。

        该技术对 for 循环的控制变量非常有用且可取,但在这种情况下,我认为这是不明智的,并且不利于清晰。当然它不起作用! ;)

        if( <type> <identifier> = <initialiser> ) // valid, but not that useful IMO
        
        if( (<type> <identifier> = <initialiser>) <operator> <operand> )  // not valid
        
        for( <type> <identifier> = <initialiser>; 
             <expression>; 
             <expression> )  // valid and desirable
        

        在您的示例中,您在条件中调用了一个具有副作用的函数,无论您如何考虑在那里声明变量,IMO 都是一个坏主意。

        【讨论】:

        • Err,你不会通过这样做将结果read(socket) &lt; 0 放入变量 i 中吗?
        • 完全正确;这就是我所说的——它改变了意思。关键是它可以编译,支持在条件中声明变量,但不在表达式中间。括号将它放在表达式的中间。有效的是带有初始化表达式的声明。
        • 至少有一组用户会阅读你的前两行,认为你错了,然后投票给你。可能值得更改第一行,以便突出显示您并不是在暗示这“解决了问题”。
        • 我不会因为一些 StackOverflow 投票而失眠,除非它们可以兑换成航空里程! ;) 但是点了。
        【解决方案7】:

        补充 RedGlyph 和 Ferruccio 所说的话。 也许我们可以执行以下操作以在条件语句中声明以限制其使用:

        if(int x = read(socket)) //x != 0
        {
          if(x < 0) //handle error
          {}
          else //do work
          {}
        }
        else //x == 0  
        {
          return true;
        }
        

        【讨论】:

        • 我喜欢它,我没想过这样重新排列我的 if 语句 :)
        【解决方案8】:

        为了补充其他人的好答案,您始终可以用大括号限制变量的范围:

        {    
          const int readResult = read(socket);
          if(readResult < 0) {
            // handle error
          } 
          else if(readResult > 0)
          {
            // handle input
          } 
          else {
            return true;
          } 
        }
        

        【讨论】:

          【解决方案9】:

          虽然与问题没有直接关系,但所有示例都将错误处理放在首位。由于有 3 种情况(>0 -> 数据,==0 -> 连接关闭和 错误),这意味着获取新数据的最常见情况需要两次测试。首先检查 >0 会将预期的测试数量减少近一半。不幸的是,White_Pawn 给出的“if(int x = read(socket))”方法仍然需要对数据的情况进行 2 次测试,但 C++17 的提议可以用于首先测试 >0。

          【讨论】:

            猜你喜欢
            • 1970-01-01
            • 1970-01-01
            • 2022-01-17
            • 1970-01-01
            • 1970-01-01
            • 2017-07-20
            • 1970-01-01
            • 1970-01-01
            • 2010-09-13
            相关资源
            最近更新 更多