为什么std :: basic_ostream :: operator <<不符合const资格?

时间:2018-07-27 16:48:20

标签: c++ c++11 const ostream

首先,用一个示例来说明我的问题背后的道德观念:以下代码将无法编译,因为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();
}

但是,我不确定为什么应该首先发生这类错误,即使运算符<<在逻辑上const中也是如此。 是的,显而易见的答案是“ operator<<以某种方式改变了std::ostream的内部状态”。

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

改写,

为什么逻辑上const std :: basic_ostream :: operator <<没有const限定,而不是使某些成员可变?

谢谢。

3 个答案:

答案 0 :(得分:4)

std::ostream 在以外部可见的方式调用operator<<之后会有所不同。在大多数情况下,tellp方法将返回更新后的值(同样,使用seekp作为第二个参数的cur的结果也会有所不同。

即使它是流输出(因此“位置”是一个无意义的概念),各种状态位也始终可以更改,并且通过good,{{1}成为外部可见状态的一部分},badfail方法。

此外,eof的缓冲区具有的行为(包括被完全换出的能力)在写入内容时会发生不可预知的变化。交换后备缓冲区的行为完全取决于其是否为空。如果为非空,则不会写入某人写入的数据。由于写入更多类型的数据可以从空状态切换到非空缓冲区状态(将少于缓冲区大小的字节写入空缓冲区而没有隐式刷新),也可以从非空状态切换为空(如果通过填充缓冲区触发刷新)缓冲区或行缓冲流上的换行符),您总是在更改缓冲区的可见状态。

答案 1 :(得分:4)

您说:

  

我还知道std::ostream的实例在调用其operator<<之前和之后在逻辑上不会有任何区别。

这是查看std::ostream的一种方法。另一种看待它的方法是它是设备的接口,即文件,控制台,字符串等。如果该接口中的成员函数更改了基础设备,则使该成员函数成为{ {1}}成员函数。

const的概念是概念性的。看一下one my answers,它对主题有一些探索。是的,可以使const函数与operator<<对象一起使用,但更有意义的是它们并非如此。他们正在更改为其提供接口的基础设备,而IMO最好与类型为const std::stream的非const对象一起使用。

答案 2 :(得分:0)

让我们看看这样的代码:

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

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

注意:添加了可变成员以优化const方法调用而无需更改可观察到的行为(昂贵的操作的缓存结果,如果对象的状态未更改,则返回缓存的值),可以在此处使用mutable只是对const方法概念的滥用