-2

The question: Why can a const member function sometimes modify a data member and sometimes not?

The explanation: The code below is an excerpt from working code in my baseline at work.

I have a Calculator class that owns a data member called "theLayout" (Header and Implementation defined below). The calculator class has a const member function called "Parms()" which returns a smart pointer to a Parms object that theLayout owns (by calling its own Parms() function).

The calculator class has a const member function called calculateStart() which sets (i.e. modifies) the reference returned from calling the Parms() function in the Calculator class.

This seems to contradict the meaning of const to me. If the const member function cannot modify the this pointer, then why can it set a value on one of the data members (theLayout) that it owns? Doesn't this "modify" the this pointer of the Calculator instance, and thus contradict the meaning of a const member function? Does this work because theLayout is a pointer?

Calculator Class

//Header
class Calculator
{
public:
   Calculator();
   //ParmsPtr is refcounted smart pointer
   const ParmsPtr& parms() const {return theLayout->parms();} 

protected:
   void calculateStart() const; //Why does this work?
   //It seems more intuitive that this should be declared as:
   void calculateStart() //with no const modifier.

   Layout&  theLayout;
}

//Implementation
void Calculator::calculateStart() const
{
   parms()->setStart(1);
}

Layout Class

//Header
class Layout : public RefCountedObject
{
public:
   Layout();
   //ParmsPtr is refcounted smart pointer 
   inline const ParmsPtr& parms() const;

private:
   ParmsPtr theParms;
}

//Implementation
inline const ParmsPtr& Layout::parms() const
{
   if (!theParms)
   {
      Layout* nonConstThis = const_cast<Layout*>(this);
      ParmsPtr parms = new Parms();
      nonConstThis->setParms(parms);
   }

   return theParms;
}
KyleEnglish
  • 35
  • 1
  • 1
  • 9
  • Bogus code is bad (e.g. `Private:`, c++ is case sensitive) Don't provide bogus code when asking but provide a [MCVE] in your question please. – πάντα ῥεῖ Apr 20 '16 at 19:05
  • How is this bogus code? It's real code. – KyleEnglish Apr 20 '16 at 19:10
  • there is `mutable` that can be used to implement lazy initialization. Your const cast looks a bit fishy to me. Actually I dont understand your question at all, after casting away the const of course you can call non const methods on `nonConstThis`, a much better question is imho why are you doing this?# – 463035818_is_not_an_ai Apr 20 '16 at 19:10
  • 1
    @Kentheglish _"How is this bogus code ..."_ Show which compiler actually accepts _`Private: ParmsPtr theParms;`_ please! – πάντα ῥεῖ Apr 20 '16 at 19:14
  • theLayout is a reference but you are treating it like a pointer with `->` ? is this code even the actual code you are using? because there is a difference between a const pointer and pointer to const – johnbakers Apr 20 '16 at 19:21
  • Hi @johnbakers, it's a reference to a smart ptr hence the usage of the `->`. It's not _exactly_ the code, but very similar. – KyleEnglish Apr 20 '16 at 19:34
  • Hi @tobi303. Thanks for the feedback. They have to cast away constness to call the setParms function, which actually highlights my original question. `setParms()` is an inline non-const function: `inline const void Layout::setParms(const ParmsPtr& parms)`. It makes sense that it is a non-const function and can thus set a data member. Compare this with the `calculateStart()` method which performs a very similar function only it's a const function! Why does this work??? – KyleEnglish Apr 20 '16 at 19:54
  • Someone up-vote me please, you guys are killing my stats! – KyleEnglish Apr 20 '16 at 19:56
  • `calculateStart` is also a quite contrived example, as it does not directly modify a member of the class – 463035818_is_not_an_ai Apr 20 '16 at 20:01
  • why downvoting this question ? it raises tricky problems with const qualifier uneasy to understand, as shown by some answers and comments here.... – shrike Apr 20 '16 at 21:03
  • Thank you @shrike! I know it's not the perfect question, but const qualifiers are super confusing. It seems like it should be cut an dry, but there is a lot of nuance with `const` that I see alot of the code using, but that just doesn't seem intuitive. – KyleEnglish Apr 20 '16 at 21:13

2 Answers2

1

The calculator class has a const member function called calculateStart() which sets (i.e. modifies) the reference returned from calling the Parms() function in the Calculator class.

Yes, calculateStart() does modify the Calculator object, which is unexpected considering the final const qualifier in its definition.

Why ? Look at definition of inline const ParmsPtr& Layout::parms() const; the final const tells the compiler that the function will not modify the Layout object, though it actually does. How ? By mischeviously const_cast'ing the object to a non-const object; that's where the constness is broken and that's why setParms() can be called.

This is a bad practice, though there may be some reasons to do it. In such cases, const_cast serves this purpose.

shrike
  • 4,449
  • 2
  • 22
  • 38
-2

The critical question is, what exactly is being made const by declaring start() to be const? The answer is the reference, not the value referenced.

In your example, inside the start() method, the compiler sees the data member as having type Layout const&, which is not the same as const Layout&. The same thing applies to pointers. If your data member was a pointer type, the compiler would see the type as Layout const*.

  • You should also note that you can make *both* const. As in `const Layout * const Layout` for example. – Jesper Juhl Apr 20 '16 at 19:18
  • you say: "Layout const&, which is not the same as const Layout&" but see this: http://stackoverflow.com/questions/3694630/c-const-reference-before-vs-after-type-specifier – johnbakers Apr 20 '16 at 19:18
  • `Layout const&`and `const Layout&` are the same, they both are a reference to a const Layout. Not to be confused with `const Layout*` and `Layout* const` which are different: non-const pointer to const Layout in 1st case, const pointer to non-const Layout in 2nd case. – shrike Apr 20 '16 at 21:07
  • Actually, in a const member it would see it as `Layout * const`. – Edward Strange Apr 20 '16 at 21:16