【问题标题】:Why does `basic_ios::swap` only do a partial swap?为什么 `basic_ios::swap` 只进行部分交换?
【发布时间】:2011-11-16 06:57:01
【问题描述】:

C++11 §27.5.4.2/21:

void swap(basic_ios& rhs);

效果: *thisrhs 的状态应该交换,除了 rdbuf() 应该返回与函数调用之前返回的值相同的值,rhs.rdbuf() 应该返回与函数调用之前返回的值相同。

这种部分交换有什么用处?

会不会惹麻烦?

【问题讨论】:

  • Alf P. Steinbach 提出问题。逆天。 :|
  • 这真是令人惊讶。如果我交换两件东西,我真的希望它们交换;如果我有一个错误并且发现它没有交换rdbuf,我会认为这是一个实现错误。
  • 确实令人惊讶。我检查了我拥有的第一个 FinalDraft (n3092),它完全一样。我想知道它在 C++03 中是否相同,也许是遗留遗留物?
  • 这会使swap 与移动分配“不一致”吗?还是移动流也会留下缓冲区?如果不一致,那么如果有人在泛型代码中假设对于任何类型Tswap(t1,t2) 的最终结果与T t3(move(t1)); t1 = move(t2); t2 = move(t3); 的最终结果相同,则可能存在潜在问题。

标签: c++ c++11 swap


【解决方案1】:

这件事你可以怪我。委员会试图改变(我认为是两次),但每次解决方案都以失败告终。

交换和移动语义在我们的 I/O 系统设计十年后进行了改进。而且这不是一个完美的合身。

注意basic_ios::swap 是一个受保护的 成员函数,并且没有命名空间范围的变体。因此,这只能从派生类(通常是 istream/ostream)中调用。请注意,i/o_stream::swap 也受到保护,并且没有命名空间范围的变体。他们的规范是调用基类swap,然后交换任何本地数据(例如istream中的gcount)。

最后在string/filestream 级别,您会得到您认为“正常”的swap:公共成员和命名空间范围的变体。在这个级别,您有一个数据成员string/file bufferrdbuf)和基类。此级别的swap 只是交换基成员和数据成员。

所有这一切的复杂特征是基类中的rdbuf()实际上是一个自引用指针,指向派生类的streambufbasic_filebufbasic_stringbuf)和那个 是您不希望基类交换这些自引用指针的原因。

这使得基础swap 很奇怪,但除了派生客户端之外,每个人都受到保护。派生客户端的swap 的代码随后看起来很简单。在派生级别,swap 被公开并以公共客户期望的方式运行。

类似的舞蹈用于动作构建和动作分配。由于基类是虚拟基类,因此移动构造更加复杂,因此其构造函数不会被最直接的派生类调用。

这很有趣。看起来很奇怪。但它最终会奏效。 ;-)

轻微修正:

Alberto Ganesh Barbati is responsiblei/ostream 级别保护swap。这是他的一个非常好的电话,我在第一个设计中完全错过了。

【讨论】:

  • 所以,tl;dr:基类不知道如何交换派生类的缓冲区,这就是为什么它把它留给派生类? basic_streambuf 中的虚拟交换功能怎么样?
  • 我还没有对那个设计进行原型设计,但我猜它可以工作。派生级别的规范看起来有点奇怪:交换你的基类而不是你的数据成员。
  • 没错,似乎基础规范或派生规范在 iostream 的当前实现中看起来很奇怪。顺便说一句,您是否考虑过重新设计 iostream 或完全用其他东西替换它们?我可以看到这会严重损害向后兼容性,但假设这没有问题.. 是否有想法在委员会中流传,例如包含在 Boost 或某事中?
  • 我个人在几年前投入了一些时间来更换 I/O 系统。但这只是一种爱好。但是我认为这里有潜力。我希望看到一些在内部自然是线程安全的(如 C),并且以更多数据驱动(而不是算法驱动)的方式(如 C)处理本地化。但保留 C++ 的类型安全绑定的类型安全和潜在的性能增强。 20-20 事后看来,我只会假设 Unicode 并放弃对非 Unicode 编码的支持。但我不知道在这方面有组织的努力。
【解决方案2】:

我只有一个推测性的答案...

如果作者假设流可能使用内部缓冲区(例如char buffer[50] 数据成员),则此规定是必要的,因为显然缓冲区的内容可能会交换,但它们的地址将保持不变。

我不知道是否真的允许。

【讨论】:

  • 可以通过rdbuf函数替换缓冲区。
  • @AlfP.Steinbach:是的,但是原始缓冲区来自哪里?它一定会被分配到某个地方,我没有发现任何阻止我为其使用内部数组的东西。
  • 缓冲区是basic_streambuf<charT, traits> 类型的对象,或派生的。它可以通过stream.rdbuf( pMyNewBuffer ) 替换。 basic_ios 析构函数被记录为不会破坏 rdbuf() 对象,因此完全可以将该对象作为直接成员,但该类仍然必须支持更改 rdbuf() 指针。
  • @AlfP.Steinbach:我没有说你不能改变它。 struct S { char buffer[64]; char* ptr; };ptr 最初指向 buffer 允许更改 ptr 同时仍然有一个内部缓冲区开始。
  • 考虑一下,是的,使用允许直接成员缓冲区的普通完全交换将防止具有不同生命周期的对象之间的交换。一个对象的缓冲区指针可能会在某个时候变得悬空。但是,如果我真的需要 swap 来解决此类问题,那么我会通过要求动态分配缓冲区并实施一些生命周期管理来解决这个问题,而不是通过定义部分 swap?
猜你喜欢
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-05-13
  • 2011-02-25
  • 1970-01-01
  • 2011-08-30
  • 2021-10-01
相关资源
最近更新 更多