在“ if”语句中初始化变量

时间:2019-07-03 08:58:55

标签: c++ c++17

我了解到,在C ++ 17中,我们可以像这样在if语句中初始化变量

if (int length = 2; length == 2)
    //execute something

代替

int length = 2;
if (length == 2)
    //do something

即使它更短,它也会影响代码的可读性(特别是对于不了解此新功能的人而言),我认为这对于大型软件开发来说是一种不良的编码实践。

除了使代码更短之外,使用此功能是否还有其他优势?

6 个答案:

答案 0 :(得分:87)

它仅将length的范围限制为if。这样您将获得我们被允许写作时最初获得的收益

for(int i = 0; i < ... ; ++i) {
   // ...
}

代替变量泄漏

int i;
for(i = 0; i < ... ; ++i) {
   // ...
}

短期生存的变量会更好,这有几个原因。但要说几对:

  1. 寿命越短,阅读不相关的代码行时需要记住的内容就越少。如果i在循环或if语句之外不存在,那么我们就不必在它们之外关注它的值。我们也不必担心其值会与程序超出其预期范围的其他部分进行交互(如果上面的i在另一个循环中重用,则可能会发生)。它使代码更易于遵循和推理。

  2. 如果变量拥有资源,那么该资源现在将保持最短的时间。而且这没有多余的花括号。还明确表明资源仅与if相关。认为这是一个激励人的例子

    if(std::lock_guard _(mtx); guarded_thing.is_ready()) {
    }
    

如果您的同事不知道该功能,请教他们!不想学习的快乐程序员是避免使用功能的不好借口。

答案 1 :(得分:20)

  

除了使代码更短之外,使用此功能是否还有其他优势?

您减小变量范围。这确实有意义并提高了可读性,因为它增强了您需要推理的标识符的位置。我同意应该避免在if语句中使用长init语句,但是对于简短内容来说,这很好。

请注意,您已经可以在C ++ 17之前的版本中对结果进行初始化和分支:

int *get(); // returns nullptr under some condition

if (int *ptr = get())
    doStuff();

这取决于个人的看法,但是您可以考虑更明确地理解明确的条件:

if (int *ptr = get(); ptr != nullptr)
    doStuff();

此外,通过提及人们不习惯使用功能这一事实来争论功能的可读性是危险的。人们在某个时候还不习惯使用智能指针,但是今天我们仍然都同意(我想)他们在那里是一件好事。

答案 2 :(得分:16)

if语句的新形式有很多用途。

  

当前,初始化器在语句之前声明,并且   泄漏到环境范围内,或使用显式范围。随着   新形式,这样的代码可以更紧凑地编写,并且经过改进   范围控制使一些以前容易出错的构造   更强大。

Open Standard Proposal for If statement with initializer

enter image description here

因此,总而言之,该语句简化了常见的代码模式,并帮助用户保持范围狭窄。

希望对您有帮助!

答案 3 :(得分:9)

为了最小化变量的范围,有一个习惯用法仅在创建时有效的情况下定义资源(例如,文件流对象):

if(auto file = std::ifstream("filename"))
{
    // use file here
}
else
{
    // complain about errors here
}

// The identifier `file` does not pollute the wider scope

有时候,您希望能够颠倒该测试的逻辑,以使失败成为主要子句,而使有效资源成为else子句。以前这是不可能的。但是现在我们可以做到了:

if(auto file = std::ifstream("filename"); !file)
{
    // complain about errors here
}
else
{
    // use file here
}

一个例子可能抛出异常:

if(auto file = std::ifstream(filename); !file)
    throw std::runtime_error(std::strerror(errno));
else
{
    // use file here
}

有些人喜欢编码,以便某个功能会在出现错误时提早中止,否则将继续执行。这个习惯用法使中止逻辑在物理上处于延续逻辑之上,而某些人可能会更自然地选择延续逻辑。

答案 4 :(得分:7)

对于逻辑事件特别有用。考虑以下示例:

public static DataTable ToDataTable(this ExcelPackage package)
{
    ExcelWorksheet worksheet = package.Workbook.Worksheets.First();

    DataTable dt = new DataTable();

    foreach (var item in worksheet.Cells[1, 1, 1, worksheet.Dimension.End.Column])
    {
        dt.Columns.Add(item.Text);
    }

    for (var rownumber = 2; rownumber <= worksheet.Dimension.End.Row; rownumber++)
    {
        var row = worksheet.Cells[rownumber, 1, rownumber, worksheet.Dimension.End.Column];
        var newrow = dt.NewRow();

        foreach (var cell in row)
        {
            // I get an exception error on this line
            newrow[cell.Start.Column - 1] = cell.Text;  
        }

        dt.Rows.Add(newrow);
    }

    return dt;
}

似乎有点粗糙。除非您对char op = '-'; if (op != '-' && op != '+' && op != '*' && op != '/') { std::cerr << "bad stuff\n"; } 的否定非常熟悉,否则您可能需要暂停思考一下此逻辑-通常这是较差的设计。借助OR, AND,您可以添加表现力。

if-initialization

命名变量也可以在char op = '-'; if (bool op_valid = (op == '-') || (op == '+') || (op == '*') || (op == '/'); !op_valid) { std::cerr << "bad stuff\n"; } 内部重用。例如:

if

这很好,特别是考虑到变量是作用域的,因此以后不会污染空间。

答案 5 :(得分:6)

这是现有功能的扩展,有助于提高我的经验的可读性。

if (auto* ptr = get_something()) {
}

在这里,我们都创建了变量ptr,并测试了它是否为非空值。 ptr的范围仅限于有效范围。要使自己确信所有ptr的使用都是有效的。

但是,如果我们谈论的是那种无法转换为bool的东西怎么办?

if (auto itr = find(bob)) {
}

那是行不通的。但是有了这项新功能,我们可以:

if (auto itr = find(bob); itr != end()) {
}

添加一个子句,指出“此初始化何时有效”。

从本质上讲,这给了我们一组令牌,它们的意思是“初始化某些表达式,当它有效时,执行一些代码。当它无效时,将其丢弃。”

自C ++ 98起,进行指针测试技巧已经很普遍了。一旦您接受了这一点,此扩展就是自然的。