检查流引用是否为空不再编译

21

我正在使用最新的gcc g++编译器(版本> 6)编译一个过时的项目。

有一个名为CodeWriter的类,其中包含一个ostream引用变量。

class CodeWriter
{
  //private:
protected:
  ostream &m_stream;
public:
  CodeWriter(ostream &stream):m_stream(stream){}
  ~CodeWriter(){
    if(m_stream != NULL){
      m_stream.flush();
    }
  }
};

由于该类规模较大,因此我仅包含了相关变量和函数。

您可以看到析构函数似乎正在将引用与NULL比较。 当我很久以前使用旧的gnu工具链编译此项目时,它可以正常编译。

但现在它会抛出错误,说没有匹配的operator !=来比较ostreamlong int

有人能解释一下这个更改背后的原理,以及我如何修复它吗?

如果需要,我可以提供额外的信息/包括整个类。


9
你误解了代码——它是将流对象与NULL进行比较(这将调用某种重载运算符)。空引用并不存在(因此,不可能检查它是否存在)。 - M.M
1
回复:“我只包含了相关的变量和函数” 是的!谢谢!完全正确! - Pete Becker
2
还要注意,在C++11或更高版本中,与空指针进行比较时,应该使用nullptr而不是宏NULL。(是的,我知道你那里没有指针,但我是在谈论你的空检查意图)。 - user439793
@Snowman 你说得对,同样的问题在这个庞大的遗留代码库的另一个部分也出现了错误,我正在疯狂地尝试修复这些问题。 - Vikash Balasubramanian
3个回答

35

这段代码并不是将引用本身与NULL比较,而是将引用的对象与NULL进行比较。引用本身不能是NULL,因此无法将引用本身与NULL进行比较。

另外,

我很久以前使用老的gnu工具链编译这个项目时,它可以成功编译。

因为自C++11以来,行为发生了变化。

在C++11之前,std::ostream可以通过operator void*()隐式转换为void*,如果流上发生错误,则返回空指针。因此,代码的原始意图是检查流是否没有错误。

自C++11以来,转换函数已更改为explicit operator bool(),如果发生错误,则返回false。请注意,该函数被声明为explicit,这意味着不允许隐式将std::ostream转换为bool,因此代码再次无法使用C++11编译,因为std::ostream无法隐式转换为bool(然后与NULL(整数字面量)进行比较)。

使用C++11兼容的编译器,您只需将代码更改为

if (m_stream) {
  m_stream.flush();
}

请注意,对于上下文转换,即使是显式的转换函数也会被考虑在内。对于上面的代码,m_stream将通过explicit operator bool()转换为bool,然后该值将用于if语句的条件。


15

流可以在布尔上下文中始终进行评估,因此只需将其更改为:

if (m_stream) {
  m_stream.flush();
}

C++11将转换为bool的过程变得explicit。这等同于if (!m_stream.fail())。在C++11之前,通过提供对void*的(隐式!)转换来实现这种简短的可检查性,这就是为什么您的旧代码以前可以工作的原因。

代码检查这一点的原因不是直接调用m_stream.flush();,而是因为流可能启用了失败时的异常,这可能会抛出异常,[更新:]但正如@Arne指出的那样,flush本身也可能失败并抛出异常。如果没有异常,则可以完全跳过布尔检查。[/更新]


1
如果 (m_stream) 等同于 if (!m_stream.fail())(允许 eof()),而不是 if (m_stream.good()),尽管对于“纯”输出流来说这是无关紧要的,因为它们不是 eof()。此外,如果意图不是抛出异常,那么该解决方案是不够的,因为刷新本身可能会导致 badbit 被设置,并且可能启用了对 badbit 的异常处理。 - Arne Vogel
2
我敢打赌,在执行flush操作之前测试流是否有效的原因与执行delete操作之前测试指针是否为空的原因相同:想要在不理解库规范的情况下“安全”。 - Pete Becker
@PeteBecker 你是在说你评论的回答还是原帖?无论哪种情况,我不确定你的意思-如果我理解正确的话,原帖作者有他自己的类,并且检查资源是否已初始化以确定是否需要刷新。我哪里没理解到? - Sebi
@Sebi:在OP的代码中没有“检查资源是否已初始化”。 - Kerrek SB
1
@Sebi - 如果一个流处于无效状态,大多数对它的操作都会变成无操作。在已经失效的流上调用flush是无害的,而测试也是毫无意义的。 - Pete Becker

8

在C++11之前,流类中的一个基类拥有一个名为operator void*()的函数。当然,这个void*的值可以与NULL进行比较。

在当前的C++标准中,这个函数被改为了explicit operator bool(),它只能在if语句中使用,而不能用于一般表达式中。

使用void*是为了避免在没有explicit操作符时发生一些不必要的bool类型转换。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接