为什么这些示例之一是未定义的行为,而另一个不是?

时间:2019-09-09 06:26:04

标签: c++ undefined-behavior

this答复的评论指出,不应使用以下代码,因为它表现出未定义的行为:

int old = (std::cin >> old, old);

here也非常鄙视类似的代码,尤其是表现出未定义的行为。

另一方面,极受好评的this则建议使用以下代码作为逗号运算符的实用性示例:

while (cin >> str, str != "STOP") {
    //process str
}

我假设如果此代码表现出未定义的行为,则不会被投票。

问题:如果第一个代码是未定义的行为(大概是由于使用了cin的读取结果而未检查后者的状态),那么第二个代码为什么会这样精细?

编辑:第一个示例的评论部分回答了问题。第二个示例未显示的是strstd::string的实例,因此已初始化。因此,没有未定义的行为。

2 个答案:

答案 0 :(得分:5)

由于C ++ 11 假定读取了非空白字符,因此语句int old = (std::cin >> old, old);定义明确。这是因为如果在这种情况下old失败,则std::cin会设置为零。使用表达式分隔符运算符,也是合法的,因为它可以对表达式std::cin >> oldold进行排序。如果没有非空白字符被计数,那么old仍不会被std::cin >> old更改,并且代码的行为是不确定的。

假设strstd::string类型,则(cin >> str, str != "STOP")始终是 定义明确的如果cin >> str失败,则保留str的初始值(可能是默认构造的),然后,再次对表达式进行排序。

答案 1 :(得分:2)

两个答案都是错误答案,但只有第一个答案可能会表现出不确定的行为。

int old = (std::cin >> old, old);

std::cin >> old失败时,old可以具有它的值。因此它可以保留为未初始化的int,因此从中读取是未定义的行为。

while (cin >> str, str != "STOP") {
    //process str
}

cin >> str失败时,str将具有其先前的值。假设strstd::string,则它不能具有与聚合类型不同的未初始化状态。因此它将具有其先前的值,例如默认构造的值""或先前读取的值"MOP"。从中读取并与"STOP"进行比较从来都不是未定义的行为。但是,cin >> str很可能会在下一个周期再次失败,因此str永远不会是"STOP",因此它将是无穷循环,所以&& is likely better than comma there

另外,值得注意的是,当我们尝试结合第一个示例和第二个示例时,我们can get real crash

std::string old = (std::cin >> old, old);

如果未将流设置为引发故障,那么我们应该手动检查那些状态(并且std::cin并不特殊),因为这些状态可能会终止并发生故障。不幸的是,许多教程示例都没有做到这一点。因此,有些人似乎认为流操作适合使用逗号运算符插入某些表达式的中间。

编辑:

我不确定评论是否与C ++ 11不同。挖了,但找不到标准的确认。在出现错误的情况下,使用g ++尝试此代码不会读取零:

#include <iostream>
#include <sstream>

int main()
{
    std::stringstream in("42");

    int i = 0;
    std::cout << i << '\n'; // outputs 0
    in >> i;
    std::cout << i << '\n'; // outputs 42
    i = 666;
    in >> i;
    std::cout << i << '\n'; // outputs 666 
    // so it did leave value of i like it was 
}

Demo。也以任何方式std::cin does not seem special。当然,可能是g ++的实现不正确,或者仅在失败子集的情况下才需要失败的输入来设置i,因此第一个示例仍可能表现出不确定的行为。