2

I have a fstream & member in a class, on which I'm calling the seekg function in a const function of the class, and yet the code compiles. I checked, and the seekg is not declared const (nor should it be), so how is this happening?

This is my code:

class Test {
    fstream &f;
public:
    Test(fstream &f_): f(f_) {}
    int fid() const {
        f.seekg(5);
        return 0;
    }
};
forumulator
  • 836
  • 12
  • 12
  • Could you elaborate on why you would expect it not to compile? – juanchopanza Jan 20 '18 at 22:20
  • 2
    The reference cannot be changed to refer to a different object. The object referred to is not `const`. – Peter Jan 20 '18 at 22:21
  • @Peter So you're saying that because reference is an inherently const, the object it's referencing here needn't be(similar to if it were a pointer)? As an aside, semantically, though, we now see that the member `f` of a class is being changed by a `const` function, how does that make sense? – forumulator Jan 20 '18 at 22:25
  • 1
    The `const` applies to an instances of `Test`, not to other objects they refer to. – juanchopanza Jan 20 '18 at 22:28
  • `const` is added at the top-level and you can't have `const` references. So `T& const -> T&`. – David G Jan 20 '18 at 22:34

2 Answers2

2

It turns out the const does not apply to members that are pointers or references, as stated here.

The best explanation I've seen of this is here where it states that inside const member functions, this is a const T *, where T is the class.

In your example that means that all the const modifier on fid() does is to change this from a Test * to a const Test * inside the function. When you write f., this is accessed as this->f. which is of type fstream & const. The reference is const but what it refers to is not, so calling a function that modifies it causes no problems.

Heath Raftery
  • 3,643
  • 17
  • 34
  • It does apply to all members (though all references are immutable anyway), but it's not transitive (a pointer / reference being `const` does not infect the pointee / referee). – Deduplicator Jul 06 '18 at 00:02
  • "transitive" - that's a very neat way to express it. I think part of the confusion is that references barely feel like indirection all, so it's not obvious that transitivity would need to apply. But they are and it does. – Heath Raftery Jul 07 '18 at 03:13
0

The rule is defined in [expr.ref]/4:

If E2 is declared to have type “reference to T”, then E1.E2 is an lvalue; the type of E1.E2 is T. [...]

In practice you should consider a reference to T, as a const pointer to T with automatic dereferencement. Internaly this is what are reference. And inside the standard, all rules that applies to reference (see [basic.life] for example) are those rules that would apply to a const pointer:

class Test {
  fstream * const f;
public:
  Test(fstream &f_): f(&f_) {}
  int fid() const {
    f->seekg(5);
    return 0;
  }
};
Oliv
  • 17,610
  • 1
  • 29
  • 72