133

这里发生了什么?

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)
{

}

如果我想在 x 设置为 false 的情况下进入“if”-body 范围,则声明需要括号(因为赋值运算符的优先级低于逻辑 OR),但由于不能使用括号,因此需要在外部声明 x身体,将该声明泄漏到比预期更大的范围。显然这个例子是微不足道的,但更现实的情况是 a 和 b 是返回需要测试的值的函数

那么我想做的事情是不符合标准的,还是我的编译器只是破坏了我的球(VS2008)?

4

8 回答 8

113

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

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

“&&”运算符是短路逻辑与。这意味着如果第一部分(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?

似乎您发现的限制似乎完全合理 - 它可以防止发生此类歧义。

于 2011-10-20T13:50:05.800 回答
106

从 C++17 开始,您尝试做的事情终于成为可能

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

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

于 2017-05-25T09:51:17.990 回答
99

ifor语句中的条件while可以是表达式,也可以是单个变量声明(带有初始化)。

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

我看不到它在哪里说不能在声明周围加上括号,也没有说每个条件只有一个声明。

6.4/1 中的语法规范给出了以下条件:

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

指定单个声明,没有括号或其他修饰。

于 2011-10-20T14:01:09.200 回答
23

如果你想将变量包含在一个更窄的范围内,你总是可以使用额外的{ }

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

    if(bool x = a || b)
    {
        //...
    }
}//a and b are out of scope
于 2011-10-20T14:12:27.417 回答
18

最后一部分已经可以了,你只需要写一点不同:

if (int a = Func1())
{
   if (int b = Func2())
   {
        // do stuff with a and b
   }
}
于 2011-10-20T16:38:40.547 回答
2

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

#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;
    }
}
于 2015-06-06T15:52:46.707 回答
1

需要注意的一件事是,较大的 if 块内的表达式

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

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

于 2011-10-25T19:45:41.513 回答
0

使用一点模板魔法,您可以解决无法声明多个变量的问题:

#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;
}

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

于 2015-01-15T23:50:28.500 回答