c++templatesc++17operator-overloadingfunction-qualifier

Can an object or its overloaded operator know if it's calling a const member function?


struct X 
{ 
  void foo () {}
  void const_foo () const {}
};
struct Y
{
  X x;
  int i;

  X* operator-> () { return &x; } 
  const X* operator-> () const { return &x; }
};

int main ()
{
  Y y;
  y->foo(); // invokes `Y::operator->`
  y->const_foo();  // invokes `Y::operator->`; BUT I want `Y::operator-> const`!!
}

As demo-ed in the code, I want the const version of operator-> to be invoked if it's used for invoking a const_foo() method.

My intention is to automate this. Hence below is NOT a solution, I am looking for:

const_cast<const Y&>(y)->const_foo(); // Don't want this explicit casting

Is there any way to achieve this?

Using templates or changing the body of X, Y or changing the declaration of their object (e.g. Y y) is fine. Just, that I don't want to use the explicit casting in the code at the places of the method invocations.


Purpose: Above is a very simplistic example. If the operator->() const is selected, then I am calling some extra code/methods within it. For any const methods of X being called such as X::const_foo(), it's desirable to call Y::operator->() const for this extra code to be invoked.


Solution

  • As @Mat commented, there is no way to implicitly select the correct overload based on return type only. You have to tell the compiler in some way which function to use at the callsite, by either calling it on a const or non-const object, or by using separate functions.

    If you want to enforce the use of a given function to access the const/non-const functions, you might want to separate this out in two different return types

    // Different return types with different interface for const/non-const overloads
    struct X {
        void foo();
    };
    struct ConstX {
        void const_foo() const;
    };
    
    struct Y {
        X* operator->();
        const ConstX* operator->() const;
    };
    

    You might also want to not overload at all and use different names for the const/non-const access to avoid const_casting

    struct X {
        void foo();
        void const_foo();
    };
    
    struct Y {
        X& x();
        const X& cx();
    };
    

    The second example can also be used in combination with the first to enforce the use of a given access function for const/non-const access.