C ++ IO流的基类std::basic_ios
定义operator void*()
以返回!fail()
,operator!()
返回fail()
。这让我想知道为什么我们需要operator!()
。当然,!is
也可以通过隐式调用operator void*()
并否定其结果来工作。
我在这里遗漏了一些东西,还是纯粹出于历史原因std::basic_ios::operator!()
被定义了?
question on comp.lang.c++.moderated也没有给出任何答案。
答案 0 :(得分:6)
对于旧的(读取:cfront
之后不久)C ++编译器,编译器无法保证在需要时隐式调用对象上的类型转换操作符。如果iostream
没有声明operator !
,那么您无法期望!cout
在所有情况下都能正常工作。 C ++ 89(或者调用前C ++ 98标准的任何东西)只是将区域定义为未定义。
这也是operator void*()
重载的原因,而不是operator int
或operator bool
。 (bool
此时标准中甚至没有作为自己的类型存在。)我记得我的教授告诉我,if()
,在C ++中预期void*
,因为该类型可以作为相对于将传递给if
语句的表达式结果类型的“超集”类型,但我没有在任何地方找到这个拼写。
这是在gcc 2的时候,当大多数人不支持模板或例外,或者如果他们这样做,并没有完全支持它们,所以使用模板进行元编程C ++仍然是一个理论练习,你确保检查operator new
是否没有返回空指针。
这让我疯了好几年。
Stroustrup的 The C ++ Programming Language ,第3版的有趣摘录。 (1997),第276页:
istream 和 ostream 类型依赖于转换函数来启用语句,例如
while (cin >> x) cout << x;
输入操作 cin&gt;&gt; x 返回 istream&amp; 。该值隐式转换为表示 cin 状态的值。然后可以通过测试该值,而。但是,通常不是一个好主意,定义从一种类型到另一种类型的隐式转换,使得信息在转换中丢失。
C ++中有很多东西似乎是可爱或聪明胜过一致的胜利。如果C ++足够聪明以处理上述循环,我不介意一点:
while (!(cin >> x).fail()) cout << x;
因为这对于初学程序员来说更加冗长,标点符号更加清晰。
......实际上,想一想,我不喜欢这些结构中的任何一个。拼写出来:
for(;;)
{ cin >> x;
if(!cin)
break;
cout << x;
}
为什么我更喜欢这个?因为这个版本使得如何将代码扩展到例如一次处理两个读取而不是一个读取更加清晰。例如,“现有代码复制一系列浮点值。我们希望您更改它,以便将浮点值配对并将它们写出来,每行两个,因为我们现在使用的是复数。”
但我离题了。
答案 1 :(得分:2)
好的,在这里干涸,我去了asked on comp.lang.c++.moderated。
起初,结果与他们在这里一样糟糕,但最终Daniel Krügler's answer同意我怀疑operator!()
没有技术原因:
我被告知,这个额外的声明被添加以强调“真实”案例和它的否定之间的对称性,仅作为读者的指南,仅此而已。公平地说,成语
operator void*
此时相当新,并且鉴于此功能提供的语法支持的推论并不是很明显。除此之外,没有其他技术理由这样做。 [...]
答案 2 :(得分:1)
查看Codeblocks附带的MinGW的实现,向我展示了这段代码:
operator void*() const
{ return this->fail() ? 0 : const_cast<basic_ios*>(this); }
bool
operator!() const
{ return this->fail(); }
在我看来,operator void*() const
旨在用于更多用途,而不仅仅是为了检查成功。最重要的是,它还可以作为一个转换运算符(我们正在返回this
)。现在我正在摸不着头脑,为什么我们可能想把this
投射到void*
。但其余的很清楚 - 如果你有一个错误的流,你也可以返回null。