Search code examples
c++recursionconstantsconst-correctnessfunction-qualifier

Does a const member function call the non-const version, or is it recursive?


For defining a second const version of a function, is it guaranteed safe to do this? It looks like it would have infinite recursion as I want to return const but the other function which I mean to call is non-const.

It works with g++ but I worry that this is unsafe.

#include <iostream>

using namespace std;

class test {
public:
   int* doSomething(int a) {
      int* someInt = new int(a);

      return someInt;
   }

   const int* doSomething(int a) const {
      return doSomething(a);
   }
};

int main() {
   test a;

   cout << *a.doSomething(12345) << endl;

   return 1;
}

Solution

  • Not quite: as @Pete Becker has pointed out in the comments, if you had called the const version that will recurse:

    class test {
    public:
       int* doSomething(int a) {
          int* someInt = new int;
          *someInt = a;
          return someInt;
       }
    
       const int* doSomething(int a) const {
          return doSomething(a);
       }
    };
    
    int main() {
       const test a;
       // You're not in for a good time:
       a.doSomething(12345);
       return 1;
    }
    

    When providing const and non-const versions of a function that requires duplicated code, it's best to implement the const version, then have the non-const version call it in a specific way.

    From Scott Myers Effective C++ - Third Edition:

    When const and non-const member functions have essentially identical implementation, code duplication can be avoided by having the non-const version call the const version

    Scott Myers goes on to provide a safe means for doing this:

    const int* doSomething(int a) const
    {
       int* someInt = new int;
       *someInt = a;
       return someInt;
    }
    
    int* doSomething(int a)
    {
       return const_cast<int*>(static_cast<const Test&>(*this).doSomething());
    }
    

    In the non-const version, there are two casts: the static_cast basically turns this into const this, where the const_cast casts away the const-ness of the return. This is safe to do, because to call the non-const version, you must've had a non-const this.

    However, if you are just providing access to a member, it's simple and easier to read to just have the following:

    class TestAccess;
    class Test
    {
        TestAccess& t;
    public:
        const TestAccess& getA() const { return t; }
        TestAcess& getA() { return t; }
    };