为什么 std::basic_ios 会重载一元逻辑否定运算符?

Why does std::basic_ios overload the unary logical negation operator?

提问人:sbi 提问时间:7/11/2010 最后编辑:sbi 更新时间:12/25/2022 访问量:594

问:

C++ IO 流的基类定义返回和返回。这让我想知道我们为什么需要它。当然,也可以通过隐式调用和否定其结果来工作。std::basic_iosoperator void*()!fail()operator!()fail()operator!()!isoperator void*()

我在这里遗漏了什么,还是纯粹是出于历史原因?std::basic_ios::operator!()

comp.lang.c++.moderated 上的问题也没有带来任何答案。

C++语言

评论

0赞 Marcelo Cantos 7/11/2010
这些是 的成员函数,而不是 。basic_iosios_base
0赞 sbi 7/11/2010
@Neil:谢谢,我忘了那个。不管怎么说,看来你也不知道?
0赞 7/11/2010
@sbi 不,我没有 - 我在Langer&Kreft上查了一下,但他们似乎也没有提供任何理由。
0赞 sbi 7/11/2010
@Neil:谢谢你这样做,但如果是在兰格/克雷夫特,我早就知道了。:)
0赞 James McNellis 7/13/2010
您可能会在 comp.std.c++ 上得到更好的响应。

答:

1赞 ablaeul 7/12/2010 #1

查看 Codeblocks 附带的 MinGW 的实现,我看到了以下代码:

  operator void*() const
  { return this->fail() ? 0 : const_cast<basic_ios*>(this); }

  bool
  operator!() const
  { return this->fail(); }

在我看来,它的目的是用于更多用途,而不仅仅是用于检查成功。最重要的是,它还可以用作铸造操作员(我们正在返回)。现在我有点挠头,为什么我们可能想投.但剩下的就很清楚了 - 如果你有一个错误的流,你也可以返回 null。operator void*() constthisthisvoid*

评论

3赞 7/12/2010
+1 但我认为这只是一种返回(在 OK 状态下)有效指针的机制,而不是一些陷阱值。
1赞 sbi 7/12/2010
@Neil:是的,我也这么认为。该标准说(在 27.4.4.3/1 中):“返回:如果那么 null 指针;否则,一些非空指针来指示成功。那只是一个安全的布尔返回.关于它说(在 27.4.4.3/2 中):“返回:。operator void*()fail()!fail()operator!()fail()
2赞 sbi 8/23/2010 #2

好的,来到这里,我去问了comp.lang.c++.moderated

起初,结果和这里一样糟糕,但最终丹尼尔·克鲁格勒(Daniel Krügler)的回答同意了我的怀疑,即没有技术原因:operator!()

有人告诉我,添加这个额外的声明是为了强调“真实”情况和它的否定之间的对称性,只是为了给读者提供指导,仅此而已。公平地说,成语

operator void*

在这个时候是相当新的,鉴于此,这个功能提供的语法支持并不是很明显的。除此之外,没有进一步的技术理由这样做。[...]

评论

3赞 James McNellis 8/23/2010
丹尼尔·克鲁格勒(Daniel Krügler)是我在在线论坛上遇到的最有帮助的人之一。如果我们能让他加入 Stack Overflow 的派对就好了......
1赞 GManNickG 8/23/2010
@James:+1。他就像litb++(没有冒犯,Johannes.<3)
0赞 sbi 8/31/2014
@James,@GMan:我年纪大了,还记得他在90年代的姓氏是Spangenberg。:-/
8赞 Mike DeSimone 8/23/2010 #3

对于旧的(阅读:不久之后)C++ 编译器,编译器不能保证在需要时隐式调用对象上的类型转换运算符。如果没有声明,那么你不能指望在所有情况下都能工作。C++ 89(或任何 C++ 之前的标准)只是将该区域保留为未定义。cfrontiostreamoperator !!cout

这也是为什么被超载了,而不是或.(当时在标准中甚至没有作为自己的类型存在。我记得我的教授告诉我,在引擎盖下,在 C++ 中期望 a,因为相对于那些将传递给语句的表达式结果类型,该类型可以充当“超集”类型,但我没有在任何地方找到这一点。operator void*()operator intoperator boolboolif()void*if

这大约是在 gcc 2 的时候,当时大多数人不支持模板或异常,或者即使支持,也不完全支持它们,所以使用模板对 C++ 进行元编程仍然是一个理论练习,你一定要检查它没有返回空指针。operator new

这让我发疯了好几年。

Stroustrup 的 The C++ Programming Language,第 3 版(1997 年),第 276 页的有趣摘录:

istreamostream 类型依赖于转换函数来启用以下语句:

while (cin >> x) cout << x;

输入操作 cin>>x 返回 istream&。该值被隐式转换为指示 cin 状态的值。然后可以通过 while 测试该值。但是,定义从一种类型到另一种类型的隐式转换通常不是一个好主意,这样就会在转换中丢失信息。

C++ 中有很多似乎是可爱或聪明战胜一致性的胜利。如果 C++ 足够聪明来处理上述循环,我不会介意:

while (!(cin >> x).fail()) cout << x;

因为这虽然更冗长,标点符号更多,但对于初学者来说更清楚。

...实际上,仔细想想,我不喜欢这两种结构。拼出来:

for(;;)
{   cin >> x;
    if(!cin)
        break;
    cout << x;
}

为什么我更喜欢这个?因为这个版本使如何扩展代码变得更加清晰,例如,一次处理两个读取而不是一个。例如,“现有代码复制浮点值序列。我们希望你改变它,这样它就可以把浮点值配对,然后写出来,每行两个,因为我们现在使用的是复数。

但我跑题了。

评论

0赞 sbi 8/23/2010
所以这是另一种解释,证实了我的怀疑,因为(当前)技术原因没有使用。至于你回答的后半部分:我认为溪流是一个非常古老的设计。与流缓冲区执行缓冲区管理和从外部设备写入/读取的流缓冲区相反,而且,不需要的东西可能不会被 boost 的同行评审所接受。operator!()good()bad()operator!()
0赞 Mike DeSimone 8/23/2010
是的,这是一个遗留的东西,尽管它是明确的而不是隐含的,可以解决一些歧义。(不,我想不出一个例子。是的,流是某人变得聪明并制造怪物的一个很好的例子。(轮班操作员超负荷工作?真?!不要让我开始<iomanip>...
0赞 Mike DeSimone 8/23/2010
就我个人而言,我对 C 和 C++ 痴迷于在同一函数中处理表示(格式化)和交付(缓冲、文件 I/O 等)感到恼火。有多少个函数的名称以 ?更好的是Python的方法,格式化和输出是完全独立的函数(C++等价物将具有函数)。printfstd::string std::format(pattern, ...)
0赞 Mike DeSimone 8/23/2010
此外,读着语言的发明者 Strostrup 写着诸如“我测试过的每个编译器都以这种方式运行”之类的话,并思考 SO 评论者会如何因为说经验证据胜过标准中所写的内容而将他撕裂四肢。
0赞 Fred Nurk 1/6/2011
@Mike:也许那些SO评论者应该反思一下它的实际影响,同时记住理论和实践之间的差异在理论上比在实践中要小。