【问题标题】:Does standard mandates that stream-constructors don't access stream buffer?标准是否要求流构造函数不访问流缓冲区?
【发布时间】:2021-01-24 08:13:47
【问题描述】:

This(相当老的)关于 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()进行任何操作。

constructor 不太清楚:

显式 basic_ostream(basic_streambuf* sb);效果: 用 basic_ios​::​init(sb) ([basic.ios.cons])。

这是否意味着这是唯一可能的效果,这个标准公式是否不允许其他效果,它们是 std::ostream 的实现细节并且可能访问未初始化的 dsb

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

【问题讨论】:

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


    【解决方案1】:

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

    是的。

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

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

    显式 basic_ostream(basic_streambuf* 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() 只是一个指针,所以这意味着传递给 init 的 sb 指针按值(即指针)将用于初始化 rdbuf()。但是,此时读取sb 指向的底层缓冲区没有副作用。

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

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


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

    请注意,[ostream.cons] 的状态自 C++ 时代以来基本没有变化;我们可以例如从the September 1994 C++ Working Paper[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&lt;charT,baggage&gt;::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() 调用来描述相同的委托。

    【讨论】:

    • 您回答的关键点是,标准中的“效果”意味着只允许这些效果。你能提供一个来源吗?例如。调用父构造函数也可能是一种效果。没有调用父构造函数吗?
    • @ead 我对库规范的解释一直是效果准确地描述了给定函数的动作,并且明确说明了任何先决条件(例如,输入迭代器或指针是否需要可解引用)。请参阅 general structure description 以及 alg.swap 的规范,其中明确要求输入迭代器作为函数的先决条件可取消引用(将出现更少的 UB)。
    • 我会假设,将指针传递给有效对象是隐含的要求。例如,ifstream(const char* s, ...); 没有明确指定 s 必须是指向有效 c 字符串 (eel.is/c++draft/ifstream#cons-2) 的指针,但是传递指向某些(未初始化的)内存的指针是 UB。
    • 另外,“效果”当然不能描述所有可能的效果。例如,在某些(大多数?)实现中,basic_ostream 的构造函数也会初始化成员变量,其中gcount 的结果存储为 0,但这是一个实现细节,标准中的“效果”下未提及.所以我不确定是否可以遵循,该标准提供了任何保证。
    猜你喜欢
    • 2017-03-24
    • 2011-11-14
    • 2021-10-06
    • 2010-12-02
    • 1970-01-01
    • 1970-01-01
    • 2017-01-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多