首页 > 解决方案 > 标准是否要求流构造函数不访问流缓冲区?

问题描述

这篇关于 iostreams 和 streambuf 的(相当老的)文章认为,以下代码是可以的:

class DerivedStreamBuf : public std::streambuf {
  // ...
};

class DerivedOutputStream : public std::ostream {
  public:
    DerivedOutputStream():
      std::ios(0), std::ostream(&dsb) {}        //1
    // ...
  private:
    DerivedStreamBuf dsb;
    // ...
};

这可能是有问题的,因为何时ostream构造dsb尚未初始化,因此 UB 可能是效果。对于析构函数,它可能是相反的:dsb已经被析构并且可以在ostream.

然而,文章认为,“C++ 标准要求没有父类的构造函数或析构函数(ios、istream 或 ostream)访问流缓冲区”。

而析构函数的情况是直截了当的,例如~ostream

虚拟〜basic_ostream();备注:不对 rdbuf() 进行任何操作。

构造函数不太清楚:

显式 basic_ostream(basic_streambuf<charT, traits>* sb); 效果:使用 basic_ios<charT, traits>​::​init(sb) ([basic.ios.cons]) 初始化基类子对象。

这是否意味着这是唯一可能的效果,这个标准公式是否不允许其他效果,这些效果是实现细节std::ostream并且可能访问 non-initialized dsb

我的问题:该标准是否保证上述代码是可移植的,即适用于所有符合标准的实现std::ostream

标签: c++c++11language-lawyer

解决方案


该标准是否保证上述代码是可移植的,即适用于所有符合标准的实现std::ostream

是的。

在调用std::ostream(&dsb)时,dsb成员DerivedOutputStream分配但未初始化。意思是,我们可以通过我们可能还没有读取它的(未初始化的)值来获取它的地址。

如问题中所链接,[ostream.cons]指定 的构造函数std::basic_ostream,特别是在您的示例中选择的构造函数是[ostream.cons]/1

显式 basic_ostream(basic_streambuf<charT, traits>* sb);

效果:basic_­ios<charT, traits>​::​init(sb)使用([basic.ios.cons])初始化基类子对象。

后置条件: rdbuf() == sb .

[tab:basic.ios.cons]描述了调用的效果,void init(basic_streambuf<charT, traits>* sb)特别是上面构造函数中描述的后置条件:

rdbuf() == sb

其余部分不执行对指向的底层缓冲区的读取访问sb

现在,rdbuf()只是一个指针,所以这意味着按值sb传递给 init的指针(即指针)将用于初始化。但是,此时读取指向的底层缓冲区没有副作用。rdbuf()sb

[...] 不允许其他效果,...?

实际上,特别是void init(basic_streambuf<charT, traits>*)函数的实现需要尊重而不是扩展[tab:basic.ios.cons]的明确指定的效果。


这篇(相当古老的)文章...

请注意,[ostream.cons] 的状态自 C++ 时代以来基本没有变化;例如,我们可以看看1994年9 月 C++ 工作论文中的[lib.input.output]

27.2.4.1.1 basic_ostream 构造函数 [lib.basic.ostream.sb.cons]

basic_ostream(basic_streambuf<charT,baggage>* sb);

1 构造类 basic_ostream 的对象,通过调用为基类分配初始值basic_ios<charT,baggage>::init(sb)

27.1.3.1.34 basic_ios::init [lib.basic.ios::init]

void init(basic_streambuf<charT,baggage>* sb_arg);

1 该函数的后置条件如表 8 所示:

                      Table 8--init effects

               +----------------------------------+
               |Element            Value          |
               +----------------------------------+
               |sb        sb_arg                  |
               |tiestr    a null pointer          |
               |state     goodbit  if  sb_arg  is |
               |          not   a  null  pointer, |
               |          otherwise badbit.       |
               |except    goodbit                 |
               |fmtfl     skipws | dec            |
               |wide      zero                    |
               |prec      6                       |
               |fillch    the space character     |
               |loc       new   locale(),   which |
               |          means the default value |
               |          is the  current  global |
               |          locale;9)               |
               |iarray    a null pointer          |
               |parray    a null pointer          |
               +----------------------------------+

init()它通过具有明确定义的效果的调用来描述相同的委托。


推荐阅读