28

在 Stroustrup 的The C++ Programming Language: Special Edition (3rd Ed)中,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++(我调用的标志是-gand -Wall)。使用这些标志编译时,我的 g++ 版本返回以下编译错误:

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

在进一步的研究中,我发现我似乎并不是唯一一个拥有不支持此功能的编译器的人。这个问题似乎有些混淆,究竟什么语法在语言中应该是标准的,什么编译器用它编译。

所以问题是,哪些编译器支持这个特性,需要设置哪些标志才能编译?这是在某些标准中而不是在其他标准中的问题吗?

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

4

9 回答 9

18

允许在嵌套块的控制部分声明变量,但在 and 的情况下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;
}
于 2009-10-04T17:46:32.570 回答
12

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

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

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

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

于 2009-10-04T17:45:12.463 回答
6

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

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

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

于 2009-10-04T21:45:08.750 回答
6

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

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

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

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

于 2017-02-17T16:52:23.513 回答
4

我遇到了类似的问题

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

if (int i = read(socket)) {

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

于 2009-10-04T17:43:54.907 回答
3

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

if(int x = read(socket)) //x != 0
{
  if(x < 0) //handle error
  {}
  else //do work
  {}
}
else //x == 0  
{
  return true;
}
于 2009-11-11T14:39:16.483 回答
3

虽然可以将声明用作布尔表达式,但不能将声明放在表达式的中间。我不禁认为您误读了 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 都是一个坏主意。

于 2009-10-04T17:54:11.510 回答
0

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

于 2017-04-03T19:26:36.857 回答
0

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

{    
  const int readResult = read(socket);
  if(readResult < 0) {
    // handle error
  } 
  else if(readResult > 0)
  {
    // handle input
  } 
  else {
    return true;
  } 
}
于 2017-02-17T17:00:38.410 回答