21

I'm trying to call const function inside a class, but a non-const function with the same name exists.

Note: I can't just change names.

class MyQuestion
{
 void fun()
 {
   cout<<"a"; 
 }

 void fun()const
 { 
   cout<<"b"; 
 }

 void call()
 {
   fun();//<how to call fun() const?
 }
};
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
dawid
  • 270
  • 2
  • 6
  • 7
    It will call one or the other automatically depending on the const-ness of the `*this` pointer – Noel Dec 05 '14 at 12:01
  • Relevant answer to a not-quite-duplicate question: http://stackoverflow.com/a/5326238/10077 – Fred Larson Dec 05 '14 at 15:39
  • 3
    That you need to call the const version of the function just seems so wrong. It means that while your const and non-const member functions happen to have the same name, they are in fact doing fundamentally different things. – David Hammen Dec 06 '14 at 00:12

4 Answers4

25

Option #1:

Call that function through a pointer to a const qualified type:

void call()
{
    static_cast<const MyQuestion*>(this)->fun();
    //          ~~~~^
}

:

void call()
{
    const auto* that = this;
    //~~^
    that->fun();
}

:

void call()
{
    std::as_const(*this).fun();
    //   ~~~~~~~^
}

Option #2:

Make the calling function a const-qualified one:

void call() const
//          ~~~~^
{
    fun();
}

DEMO

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
  • 1
    I don't really like the verbosity of the static cast (to explicitly mention the class name). You can avoid that by using a local variable with type `const auto*`. – leemes Dec 05 '14 at 12:25
  • 1
    Why would a static_cast be appropriate for this, and not a const_cast? –  Dec 05 '14 at 20:19
  • 3
    @Thebluefish `const_cast` usually indicates casting away the *constness* (I'm doing the opposite here), I'm not fond of using it for regular implicit conversions, `const_cast` does its cast unconditionally – Piotr Skotnicki Dec 05 '14 at 20:23
  • I would use an `implicit_cast` instead of a `static_cast` there (or use a local as @leemes proposes). Even if I have to write it for myself, or import it from boost... – Deduplicator Dec 05 '14 at 23:22
  • Rather than use the case I prefer to use a const reference internally. `MyQuestion const& self = *this; self.fun()` – Martin York Dec 06 '14 at 06:24
18

You have to call the function on a const pointer. For this, I recommend to create a local pointer variable:

const auto *c = this;
c->fun();   // calls fun() const

fun();      // calls fun()

Live Demo


If you need that often, and/or if you don't want to use a local variable, you could also introduce a private helper function which returns a const this pointer:

const MyQuestion *const_this() const {
    return this;
}

and then call fun like this:

const_this()->fun();   // calls fun() const

fun();                 // calls fun()

Live Demo


Yet another option is to write a make_const function which performs a cast to a const pointer without the need to mention the class name (it's basically a static_cast to a const pointer of a deduced type):

template <typename T>
const T* make_const(T *ptr) {
    return ptr;
}

and then call fun like this:

make_const(this)->fun();    // calls fun() const

fun();                      // calls fun()

Live Demo


For the sake of argument (I don't recommend the following), combining with the suggestion above, you could also introduce a global macro which expands to make_const(this):

#define  const_this  make_const(this)

and then call fun like this:

const_this->fun();   // calls fun() const

fun();               // calls fun()

Live Demo

leemes
  • 44,967
  • 21
  • 135
  • 183
  • The lambda requires C++14 or an explicit return type. C++11 can only deduce it for lambdas which contain nothing but a single `return` statement. – Angew is no longer proud of SO Dec 05 '14 at 12:43
  • @Angew Oh, didn't know that, as g++ allows other statements and I always used lambdas this way... But good to know, thanks :) -- Updated the macro to expand to a function call instead. – leemes Dec 05 '14 at 13:48
  • @angew The "only a single line with a return statement" restriction lasted for almost zero time in every major compiler. C++14 with the restriction removed was on its way, and it was just that easy... – Yakk - Adam Nevraumont Dec 06 '14 at 01:24
  • 1
    I call `make_const` `as_const` in my codebase. And add a `T&` and `T const&` overload. – Yakk - Adam Nevraumont Dec 06 '14 at 01:25
  • 1
    @Yakk It seems that with C++17 there is said `as_const()` function in `std::`: http://en.cppreference.com/w/cpp/utility/as_const So one does not have to add yet another function to the ever growing list of global utility functions per project. – user2460318 May 27 '16 at 06:26
4

I would like to add another possible solution to the excelent ones already posted.

You can help the compiler to choose the correct overload using a function pointer with the expected signature:

// Pointer to the version of fun with const
void (MyQuestion::*f)()const = &MyQuestion::fun;
(this->*f)(); // This would call the const fun

See the demo here, or the full code below:

struct MyQuestion
{
    void fun()
    {
        std::cout<<"a"; 
    }

    void fun()const
    { 
        std::cout<<"b"; 
    }

    void call()
    {
        void (MyQuestion::*f)()const = &MyQuestion::fun;
        (this->*f)();
    }
};

Why does this work?

Well, the type of the f pointer is void (MyQuestion::*)()const which is the same of MyQuestion::foo()const but not the same of MyQuestion::foo(), so when you take te address of the function &MyQuestion::fun the pointer f could only point to the const version.

PaperBirdMaster
  • 12,806
  • 9
  • 48
  • 94
3

how about overloading call() itself. Following implementation does the job. I m guessing that it is what you want to implement.

#include <iostream>
using namespace std;

class A
{
public:
    void fun() { cout << "non" << endl; }
    void fun() const { cout << "const" << endl; }

    void call() { fun(); }
    void call() const { fun(); }
};

int main()
{
    A a;
    a.call();

    const A b;
    b.call();
    return 0;
}
nishparadox
  • 750
  • 6
  • 13
  • This may not work -- `call()` may have very good reasons *not* to be const! – LThode Dec 05 '14 at 19:13
  • the const call() will only be called only when the object being created is a const which will definitely invoke the const fun() – nishparadox Dec 06 '14 at 02:22
  • How do you know that `call()` and `fun()` are members of the same class? We could have a scenario where `A::call()` needs to be non-const because it mutates the A object its working on, but needs to get something from a B referenced by the A object using `B::fun() const` where the non-const `B::fun()` does something unwanted to the B object. – LThode Dec 08 '14 at 15:05