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


80

我读到在C ++ 17中我们可以在这样的if语句中初始化变量

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

代替

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

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

除了使代码更短之外,使用此功能还有什么好处?


38
除了范围的东西?
DeiDei

10
我猜想几年前有人说过“我在C ++ 11中读到,我们可以创建这样的lambda语句(...),尽管它较短,但会影响代码的可读性(尤其是对于不了解代码的人这个新功能),对于大型软件开发,我认为这是一种不良的编码做法。”
R2RT

9
我会说它的长度完全相同,而不是较短。
user7860670 '19

5
纯粹的见解,因此不是一个答案:if (int length = 2; length == 2)也许您第一次看到它是令人惊讶的,但这并不复杂,一个人无法理解,因此已经第二次了,这将不再是一个大惊喜,并且在它属于的范围内声明是促成可读性的主要因素之一。恕我直言,你的前提是错误的;)
idclev 463035818 '19

14
对于不了解代码编写语言的人(这就是“不知道此新功能”的意思),他们担心代码的可读性是一场白热化的竞赛。

Answers:


97

它的范围仅限lengthif一个。因此,您将获得我们被允许写作时最初获得的收益

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

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


12
我要抓住最后一句话,然后将其拍在两米长的海报上。
Quentin

3
同样,短暂的(范围狭窄的)变量应该减少错误,因为一旦达到目的,您就不会在以后的代码中意外地重用该变量。
加利克

1
还是指针较弱:if (auto p = ptr.lock(); p && p->foo()) bar(*p);
Deduplicator

1
3.在更多情况下,允许编译器重用堆栈空间。(例如,如果您通过引用或指针将i传递给外部函数。)
TLW

更好的问题可能是“相对于此有什么优势{int i = 2; if (i == 2) {...}}”(请注意其他范围。)
TLW

24

除了使代码更短之外,使用此功能还有什么好处?

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

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

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

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

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

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

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


4
您可以使用,if (auto p =get ())因为已定义了运算符bool
sudo rm -rf斜杠

19

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

当前,初始化器要么在语句之前声明,然后泄漏到环境范围中,要么使用显式范围。使用新格式,可以更紧凑地编写此类代码,并且改进的范围控制使一些以前容易出错的构造更加健壮。

使用初始化程序为If语句打开标准提案

在此处输入图片说明

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

希望对您有所帮助!


您能否明确说明您正在引用该提案?特别是第二段。我建议使用引号。
StoryTeller-Unslander Monica,

感谢@StoryTeller,是的,我引用了C ++ 17中合并的open-std提案的第二段。
Abhishek Sinha

10

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

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
}

有些人喜欢编写代码,以便函数可以在出现错误时提早中止,否则将继续执行。这种习惯用法使中止逻辑在物理上置于某些人可能会觉得更自然的延续逻辑之上。


8

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

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。例如:

if (double distance = std::sqrt(a * a + b * b); distance < 0.5){
    std::cerr << distance << " is too small\n";
}

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


2
我意识到这是主观的,但是与使用if-initializer的版本相比,我强烈希望您的“粗糙”版本。我发现它更容易阅读和理解。
Fabio说恢复Monica

@FabioTurati我想那是因为您对它非常熟悉,而另一个版本是新的。但是随着时间的流逝,我希望if-initializer胜过任何类似的事情。
Stack Danny

7

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

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

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

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

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

那不行 但是有了这个新功能,我们可以:

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

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

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

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

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.