繁体   English   中英

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

[英]C++, variable declaration in 'if' expression

这里发生了什么?

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

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

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?

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

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

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

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

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

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

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

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

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

注意使用; of 而不是,将声明和实际情况分开。

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

//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
   }
}

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

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

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

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM