Puzzle #7 (based on C++17)

struct Foo
{
  Foo(int& baz) 
    : m_baz{ baz }
  {}

  void bar() const
  {
    m_baz = 2;
  }

  int& m_baz;
};

int main()
{
  int baz{};
  Foo foo{ baz };
  foo.bar();

  return baz;
}

With given code and C++17 compiler, pick one answer:

  • Guaranteed to return 2 from main.
  • Guaranteed to return something other than 2 from main.
  • Undefined behavior.
  • Implementation defined.
  • Will not compile.
Click here for the answer

The correct answer is: Guaranteed to return 2 from main.

In the body of a non-static member function, the keyword this is a prvalue whose value is the address of the object for which the function is called. The type of this in a member function of a class X is X. If the member function is declared const, the type of this is const X, if the member function is declared volatile, the type of this is volatile X, and if the member function is declared const volatile, the type of this is const volatile X.

And we have an interesting example there:

struct s {
  int a;
  int f() const;
  int g() { return a++; }
  int h() const { return a++; } // error
};

int s::f() const { return a; }

The a++ in the body of s::h is ill-formed because it tries to modify (a part of) the object for which s::h() is called. This is not allowed in a const member function because this is a pointer to const; that is, *this has const type.

Thanks to that we know that in const methods, the implicit this parameter is in fact const this, which means that we can not change any part of the object to which this points. (Un)likely, in our code, we don’t change part of the object. Through the reference, we change object that is not part of our *this, thus the compiler allows us to shot ourselves in knee change value of baz.

Thanks for reading o/