C + + ,“ if”表达式中的变量声明

这是怎么回事?

if(int a = Func1())
{
// Works.
}


if((int a = Func1()))
{
// Fails to compile.
}


if((int a = Func1())
&& (int b = Func2()))
)
{
// Do stuff with a and b.
// This is what I'd really like to be able to do.
}

2003年标准中的6.4.3节解释了在选择语句条件中声明的变量如何具有扩展到条件控制的子语句末尾的作用域。但是我没有看到任何地方提到不能在声明前后加上括号,也没有提到每个条件只有一个声明。

即使在条件中只需要一个声明的情况下,这种限制也很烦人。

bool a = false, b = true;


if(bool x = a || b)
{


}

如果我想输入‘ If’-body 作用域,并且 x 设置为 false,那么声明就需要括号(因为赋值操作符的优先级比逻辑 OR 低) ,但是由于不能使用括号,所以它需要在主体之外声明 x,这样声明的作用域就会比预期的要大。显然,这个示例很简单,但是更现实的情况是,a 和 b 是函数,返回需要测试的值

那么,我想要做的是不符合标准的,还是我的编译器只是在捣乱(VS2008) ?

91937 次浏览

我想你已经暗示过这个问题了。编译器应该如何处理这段代码?

if (!((1 == 0) && (bool a = false))) {
// what is "a" initialized to?

“ & &”操作符是一个短路逻辑 AND。这意味着,如果第一部分 (1==0)是假的,那么第二部分 (bool a = false)就不应该被计算,因为已经知道最终的答案是假的。如果没有计算 (bool a = false),那么以后如何处理使用 a的代码?难道我们就不能初始化这个变量,让它保持未定义状态吗?我们将它初始化为默认值吗?如果数据类型是一个类,并且这样做会产生不良的副作用呢?如果你使用的是一个类而不是 bool,而且它没有缺省构造函数让用户 必须的提供参数——那么我们该怎么办?

下面是另一个例子:

class Test {
public:
// note that no default constructor is provided and user MUST
// provide some value for parameter "p"
Test(int p);
}


if (!((1 == 0) && (Test a = Test(5)))) {
// now what do we do?!  what is "a" set to?

看起来你所发现的限制似乎是完全合理的——它防止了这种模棱两可的情况发生。

ifwhile语句中的条件可以是 表情,也可以是单个变量 声明(带初始化)。

第二个和第三个示例既不是有效表达式,也不是有效声明,因为声明不能构成表达式的一部分。虽然能够像您的第三个示例那样编写代码是有用的,但是需要对语言语法进行重大更改。

我不明白它在哪里说不能在声明前后加上括号,也没说每个条件只能有一个声明。

6.4/1中的语法规范为条件提供了以下内容:

condition:
expression
type-specifier-seq declarator = assignment-expression

指定单个声明,不带括号或其他装饰。

如果希望在较窄的范围内封装变量,总是可以使用额外的 { }

//just use { and }
{
bool a = false, b = true;


if(bool x = a || b)
{
//...
}
}//a and b are out of scope

最后一部分已经可以了,你只需要稍微改动一下:

if (int a = Func1())
{
if (int b = Func2())
{
// do stuff with a and b
}
}

需要注意的一点是,在较大的 if 块中的表达式

if (!((1 == 0) && (bool a = false)))

不一定保证以从左到右的方式进行评估。我以前遇到的一个很微妙的错误与编译器实际上是从右向左测试而不是从左向右测试有关。

使用一个小小的模板魔法,你就可以绕过不能声明多个变量的问题:

#include <stdio.h>


template <class LHS, class RHS>
struct And_t {
LHS lhs;
RHS rhs;


operator bool () {
bool b_lhs(lhs);
bool b_rhs(rhs);
return b_lhs && b_rhs;
}
};
template <class LHS, class RHS>
And_t<LHS, RHS> And(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }


template <class LHS, class RHS>
struct Or_t {
LHS lhs;
RHS rhs;


operator bool () {
bool b_lhs(lhs);
bool b_rhs(rhs);
return b_lhs || b_rhs;
}
};
template <class LHS, class RHS>
Or_t<LHS, RHS> Or(const LHS& lhs, const RHS& rhs) { return {lhs, rhs}; }


int main() {
if (auto i = And(1, Or(0, 3))) {
printf("%d %d %d\n", i.lhs, i.rhs.lhs, i.rhs.rhs);
}
return 0;
}

(注意,这会失去短路评估。)

下面是一个使用循环(如果两个变量都是整数)的丑陋解决方案:

#include <iostream>


int func1()
{
return 4;
}


int func2()
{
return 23;
}


int main()
{
for (int a = func1(), b = func2(), i = 0;
i == 0 && a && b; i++)
{
std::cout << "a = " << a << std::endl;
std::cout << "b = " << b << std::endl;
}


return 0;
}

但是这会使其他程序员感到困惑,而且这是相当糟糕的代码,所以不推荐使用。

一个简单的封闭 {}块(已经推荐过了)更容易阅读:

{
int a = func1();
int b = func2();


if (a && b)
{
std::cout << "a = " << a << std::endl;
std::cout << "b = " << b << std::endl;
}
}

从 C + + 17开始,你尝试做的 终于有可能了:

if (int a = Func1(), b = Func2(); a && b)
{
// Do stuff with a and b.
}

注意,使用 ;而不是 ,来分隔声明和实际条件。