1

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;
}
Zhro
  • 2,546
  • 2
  • 29
  • 39
  • 3
    This code calls the non-const version of `doSomething`. Try calling the const version and watch it blow up. – Pete Becker Jan 25 '16 at 00:56
  • Why don't you just initialize `someInt` when allocating it: `int* someInt = new int(a);` – erip Jan 25 '16 at 00:58
  • i always thought c++ doesnt allow overloading of functions that only differ in return type.... – Anedar Jan 25 '16 at 00:59
  • 1
    @Anedar: The overload doesn't only differ in return type. It also has different cv-qualification. – IInspectable Jan 25 '16 at 01:05
  • @Anedar - it doesn't. But that's not the case here; the two functions differ in their `const` qualifiers, which determines which one can be called on a const object and which one on a non-const object. – Pete Becker Jan 25 '16 at 01:06

2 Answers2

3

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; }
};
Tas
  • 7,023
  • 3
  • 36
  • 51
  • Scott Myers's solution is perfect. I was trying to think if there was a way to call the non-`const` version in some way from the `const` one, which I don't think you can. Thank you for explaining this clearly. Part of the problem was my misunderstanding of how a const method works: http://stackoverflow.com/questions/34983475/why-is-stdbasic-stringoperator-a-const-method-if-its-also-a-non-const-met?answertab=active#tab-top – Zhro Jan 25 '16 at 01:31
  • Any caveats to having the `const` version call a non-`const` version? Seems to work OK. – Zhro Jan 25 '16 at 03:58
  • Yes: a `const` member function promises to never change the state of an object, but you'd essentially be calling a non-`const` function which could alter the state of the object. It's safer to have the non-`const` version call the `const` version, as a non-`const` function can do whatever it wants to an object. – Tas Jan 25 '16 at 04:11
  • You're right but from the perspective of simply removing duplicate code it shouldn't make a difference. I do something like this: `u8char_t* codepoint = &_base[0];` which when called from a const method throws an error unless I remove the const with a static cast. I felt that if I were doing that then I may as well just have call the non-`const` version from the `const` one and forego the cast. To clarify, I use `codepoint` to iterate over the base string with `codepoint + length` where `length` is the width of the utf-8 character in bytes. – Zhro Jan 25 '16 at 04:22
0

In this case the compiler is always going to pick the not const version of the function, is not even calling the const one. Otherwise the compiler will not compile, you are braking the constenss. For example I modified quickly the code:

#include <iostream>

using namespace std;

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

        *someInt = a;
        return someInt;
    }
    int ax = 10;
    void somethingElse(int i)
    {
        ax = i;
    }
    const int* doSomething(int a) const {
        somethingElse(a);
        return 0;
    }
};

int main() {
    test a;

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

    return 1;
}

This example does not compile because you are calling a const function inside a const scope. The compiler won't let you do that.

Now, I know is a test but doing this way you will never get out of the recursion, it will loop forever, and also you are leaking memory at every call by allocating on the heap, those two things together can lead to a disaster.

Marco Giordano
  • 600
  • 4
  • 14
  • I know it's leaking memory. It's is just an example. – Zhro Jan 25 '16 at 01:07
  • Out of curiosity of your coding style, is the closing brace of `somethingElse()` indented 8 spaces for a reason or is this a mistake? – Zhro Jan 25 '16 at 01:09
  • you could just return the int itself, even for a test don't see why you need a leaking pointer. Anyway not the point. About parenthesis, yep mistake, I quickly coded it and must have been messed up in the copy pasting Cheers M. – Marco Giordano Jan 25 '16 at 01:09
  • G++ was yelling at me saying `warning: type qualifiers ignored on function return type` so I switched it to a pointer. It could be const but the result is a native type and always passed by value; so the `const` is moot. – Zhro Jan 25 '16 at 01:11
  • You can pass pointer and references to native types. but usually, unless there is a good reason for that, you should try to avoid it, just because you are using more memory then you should, on a 64 bit machine a pointer or ref to an int is 64 bit, meanwhile the int itself is most likely 32 bit. But if you have multiple arguments you use to return values then you can use a ref or pointer to native types. – Marco Giordano Jan 25 '16 at 01:13