为什么 std::basic_ostream::operator<< 不是 const 限定的?

why is std::basic_ostream::operator<< not const-qualified?

首先,举个例子来说明我的问题背后的道理:下面的代码无法编译,因为 std::basic_ostream::operator<< 不符合 const 条件。 (https://gcc.gnu.org/onlinedocs/libstdc++/libstdc++-html-USERS-3.4/ostream-source.html 表明运算符不符合 const 条件。)

我使用 GNU g++ 6.4.0 编译器编译,打开了 --std=c++11 标志。

#ifndef TEST_H
#define TEST_H
#include<string>
#include<iostream>
using namespace std;
class ChessPiece{
    const string name;
    const int side;
public:
    ChessPiece(const string&,const int);
    void printPiece(const ostream&) const;
};
#endif // TEST_H

...和test.cpp.

#include"test.h"
ChessPiece::ChessPiece(const string& s,const int bw): name{s}, side{bw} {}
void ChessPiece::printPiece(const ostream& S=cout) const{
    S << "a " << (side==1?"white ":"black ") << name << endl;
}
int main(){
    ChessPiece p{string("pawn"),-1}; // a black pawn
    p.printPiece();
}

但是,我不确定为什么首先会出现这些类型的错误,即使 operator<< 在逻辑上如上述代码所示 const。 是的,显而易见的答案是“operator<< 以某种方式改变了 std::ostream 的内部状态”。

但是,我知道通过使成员 mutable 我们可以更改 class 的内容,只要 const-限定函数在逻辑上是 const.我还知道 std::ostream 的实例在调用它的 operator<< 之前和之后在逻辑上不会有任何不同。 (如有错误请指出,谢谢)

改写,

为什么逻辑上 const std::basic_ostream::operator<< 不是 const 限定的,而不是让它的一些成员可变?

提前致谢。

std::ostream 在调用其 operator<< 后以外部可见的方式有所不同。大多数时候,tellp 方法将 return 更新值(同样,使用 cur 作为第二个参数的 seekp 的结果会有所不同)。

即使是流输出(所以"position"是一个无意义的概念),各种状态位总是可以变化的,同样,通过good是外部可见状态的一部分, badfaileof 方法。

此外,ostream 的缓冲区的行为(包括完全换出的能力)在写入内容时会出现不可预测的差异。换出后备缓冲区的行为完全不同,具体取决于它是否为空;如果非空,则不会写入某人写入的数据。由于写入任何类型的更多数据都可以从空缓冲区状态切换到非空缓冲区状态(将小于缓冲区大小的字节写入空缓冲区而没有隐式刷新),以及从非空到空(如果通过填充缓冲区触发刷新)缓冲区或行缓冲流上的换行符),你总是在改变缓冲区的可见状态。

你说:

I also know that an instance of std::ostream will not logically differ in any way before and after calling its operator<<.

这是查看 std::ostream 的一种方式。另一种看待它的方式是,它是设备的接口——文件、控制台、字符串等。如果该接口中的成员函数更改了底层设备,则使该成员函数成为 const 成员函数.

const 的概念是概念性的。看一下 ,它稍微探讨了这个主题。是的,可以使 operator<< 函数与 const std::stream 对象一起使用,但它们不是更有意义。他们正在更改为其提供接口的底层设备,IMO,他们最好使用类型为 std::ostream.

的非 const 对象

我们来看这样的代码:

os << data;
if( !os ) throw std::runtime_exception( "io error" );

虽然 operator<< 的结果通常被忽略,但可能需要检查输出是否成功。很可能编译器将无法优化此检查,但对于作为 reader 的我来说,它变得不清楚,为什么我需要在调用 const 方法后重新检查对象的状态。因此,将这些方法标记为 const 会损害界面的可读性,并且不会产生任何好处,显然这些调用在概念上不是 const

注意:为优化 const 方法调用而不改变可观察行为的能力添加了可变成员(昂贵操作的缓存结果和 return 缓存值,如果对象的状态没有改变),使用mutable 这里是对 const 方法概念的简单滥用