【问题标题】:what's wrong with declaring a variable inside if's condition?在 if 条件中声明变量有什么问题?
【发布时间】:2012-01-22 13:40:37
【问题描述】:

也许我开始生疏了(最近一直在用 Python 写东西)。

为什么不编译?

if ( (int i=f()) == 0)

没有int i=f() 周围的() 我得到另一个更合理的i 错误不是布尔值。但这就是为什么我首先想要括号!

我的猜测是使用括号使其成为表达式,并且表达式中不允许声明语句。是这样吗?如果是,它是 C++ 的语法怪癖之一吗?

顺便说一句,我实际上是在尝试这样做:

if ( (Mymap::iterator it = m.find(name)) != m.end())
    return it->second;

【问题讨论】:

  • 怎么了?一切
  • @VJovic - 如果您通过如此详尽、有用的答案获得声誉分数,我会徘徊;)
  • 不,我会否定;)但是说真的,任何正常的编码标准都禁止这种晦涩的代码。
  • for( int i = f(); i == 0; i=1) { :-) for( Mymap::iterator it = m.find(name); it != m.end(); it = m.end()) { ...(仅用作客厅技巧)
  • c++ 功能要求:[if|while]( var_decl_with_init ; expr ) statement

标签: c++ syntax variable-declaration


【解决方案1】:

您可以在 C++ 中的if 语句中声明一个变量,但它仅限用于直接初始化,并且需要转换为布尔值:

if (int i = f()) { ... }

C++ 没有任何可以被描述为“声明表达式”的东西,即声明变量的 [sub-] 表达式。

其实我只是查了一下标准中的条款,根据6.4 [stmt.select]第1段支持两种形式的初始化:

...
condition:
   expression
   attribute-specifier-seqopt decl-specifier-seq declarator = initializer-clause
   attribute-specifier-seqopt decl-specifier-seq declarator braced-init-list
...

也就是说,也可以写成:

if (int i{f()}) { ... }

显然,这只适用于 C++2011,因为 C++2003 没有大括号初始化。

【讨论】:

  • 我不认为你的最后一个例子是合法的。你只能做if (int i {f()}) { /*...*/ }if (int i = f()) { /*...*/ }braced-init-list 必须包含大括号。 (braced-init-list 是 "{ initializer-list ,OPT }" 或 "{ }")。
  • @CharlesBailey:是的,你是对的:我不知何故将“braced-init-list”误认为是(...)。我会解决这个问题。
  • 谢谢,听起来很合理,下面@Ilya 的回答给出了一些理由
  • 从你的第一句话“但它仅限于直接初始化使用它需要转换为布尔值”->你能用简单的句子解释一下吗? (我猜初始化的值是布尔值,对吧?但你的第一行也无法理解限制部分),谢谢
【解决方案2】:

范围有问题。

考虑以下代码:

if ((int a = foo1()) || (int b = foo2()))
{
    bar(b);
}

b 是在块内声明的吗?如果foo1() 返回 true 会怎样?

【讨论】:

  • 忘了提到我认为这不是范围问题,因为()AFAIK 不会创建范围。我弄错了吗?
  • @SoapBox 但是如果 foo1 返回 1 并且通过短路评估(据我所知这是有保证的)b 的分配被跳过怎么办?
  • 完全正确 - 除非您解决范围问题,否则您不能建议允许在 () 中声明变量; C++ 中的变量的作用域从它们的声明“执行”开始,在这种情况下可能永远不会发生。
【解决方案3】:

您可以在 if 语句中(或在 for 或 while 中)声明变量,但只能在外括号块中声明,并且需要能够转换为 bool。

你的猜测基本上是对的,这是不允许的,因为

(int i = 42;)

不是一个有效的初始化声明。

你需要多一行,

Mymap::iterator it;
if ( (it = m.find(name)) != m.end())
    return it->second;

但最好还是写

Mymap::iterator it = m.find(name);
if ( it != m.end() ) 
    return it->second;

你可以把return 放在if 之后,如果你真的想要这条线,至少对我来说这不会损害可读性,但其他人可能会看到不同。

如果您真的非常想声明一个迭代器并将其用作 if 条件中的布尔值,您可以这样做

if ( struct { int it; operator bool() { return it != m.end; } } s = { m.find(name) } )
    return s.it->second;

但我认为这是有害的;-)

【讨论】:

    【解决方案4】:

    确实不会写

    if ( (int i=f()) == 0)
    

    但你可以完美地写

    if ( int i=f())
    

    因此您可以使用&& 运算符在一个语句中执行这两个操作,例如

    if ( int i=1 && (i=f()) == 0)
    

    i 应该使用 0 以外的任何值进行初始化,如果您的编译器应用从左到右的评估,它应该是第一个条件。

    但不幸的是,这不适用于第二个示例所要求的迭代器。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-02-25
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多