在C ++中声明和初始化Conditional或Control语句中的变量

时间:2009-10-04 17:20:27

标签: c++ compiler-construction conditional declaration

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

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

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

然而,g ++(版本4.3.3 Ubuntu特定编译)似乎不支持编程语言的这个特性,这对我来说是令人惊讶的。也许我只是用一个标志来调用g ++来关闭它(我称之为-g-Wall的标志)。在使用这些标志进行编译时,我的g ++版本返回以下编译错误:

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

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

所以问题是,什么编译器支持这个功能以及需要设置哪些标志才能编译?是否存在某些标准而非其他标准的问题?

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

9 个答案:

答案 0 :(得分:18)

允许在嵌套块的控制部分中声明变量,但在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;
}

答案 1 :(得分:11)

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

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

这样是否声明了p,它是一个有效的指针。没有悬空指针进入危险区域。

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

答案 2 :(得分:6)

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

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

因此虽然范围不包含,但实际上并不重要,因为变量不能改变。

答案 3 :(得分:5)

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

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

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

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

答案 4 :(得分:4)

我遇到过类似的problem

问题似乎是围绕int声明的括号。如果你可以表达作业并在没有它们的情况下进行测试,那就应该有效,即

if (int i = read(socket)) {

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

答案 5 :(得分: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都是个坏主意。

答案 6 :(得分:3)

添加RedGlyph和Ferruccio所说的内容。 可能我们可以执行以下操作仍然在条件语句中声明以限制其使用:

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

答案 7 :(得分:0)

补充其他人的#39;好的答案,您总是可以通过大括号限制变量的范围:

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

答案 8 :(得分:0)

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