在 if 条件语句中声明变量有什么问题?

17

也许我有点生疏了(最近一直在写 Python)。

为什么这段代码不能编译?

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

如果在int i=f()周围没有加上(),我会得到另一个更合理的错误,即i不是布尔值。但这正是我想要括号的原因!

我猜使用括号将其变成了表达式,而声明语句不允许出现在表达式中。是这样吗?如果是,这是C++语法的怪癖之一吗?

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

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

1
什么问题?一切都有问题。 - BЈовић
5
@VJovic - 我想知道你是否通过提供详细、有帮助的回答来获得你的声誉分数 ;) - davka
不,我会变成负数的 ;) 但说真的,任何正常的编码标准都禁止使用这样晦涩难懂的代码。 - BЈовић
for (int i = f(); i == 0; i=1) {for (Mymap::iterator it = m.find(name); it != m.end(); it = m.end()) { ...(仅用于巧妙的把戏) - greggo
C++特性需求:[if|while](var_decl_with_init; expr) statement - greggo
4个回答

38

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

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

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

实际上,我刚刚在标准中查找了这个条款,并且根据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 }" 或者 "{ }")。 - CB Bailey
@CharlesBailey:是的,你是对的:我不知何故把“braced-init-list”理解为(...)了。我会修复这个问题。 - Dietmar Kühl
谢谢,听起来合理,@Ilya在下面的回答中提供了一些原理。 - davka
2
从你的第一句话开始,“但是它只限于与直接初始化一起使用 ,并且需要转换为布尔值” -> 你能简单解释一下吗?(我猜初始化的值起到布尔值的作用,对吗?但我无法理解第一行中的限制部分),谢谢。请允许我进行翻译和简化:该语句只能用于直接初始化,并且需要将其转换为布尔值才能正常工作。这意味着您必须在初始化期间提供一个布尔值,而不能在以后更改它。 - Mr.Anubis

20

作用域存在问题。

考虑下面的代码:

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

b 在这个块内部声明了吗?如果 foo1() 返回 true,会发生什么?


忘了提到,我认为这不是作用域问题,因为据我所知,()不会创建作用域。我错了吗? - davka
3
但如果 foo1 返回 1,通过短路求值(据我所知是有保证的),b 的赋值会被跳过怎么办? - lccarrasco
确切地说 - 除非处理作用域问题,否则不能建议在 () 中声明变量;在 C++ 中,变量的作用域始于其声明被“执行”的时刻,而在此情况下这种执行可能永远不会发生。 - greggo

5

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

您的猜测基本上是正确的,这是不允许的,因为

(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条件中的bool值,那么你可以这样做。
if ( struct { int it; operator bool() { return it != m.end; } } s = { m.find(name) } )
    return s.it->second;

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


0

确实无法编写

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

但你可以完美地编写

if ( int i=f())

所以你可以使用&&运算符在一条语句中执行这两个操作。
if ( int i=1 && (i=f()) == 0)

i应该初始化为任何非零值,并且如果您的编译器应用从左到右的顺序评估,那么它应该是第一个条件。

但不幸的是,在迭代器的情况下,您的第二个示例并不适用。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接