1
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.

iammilind
  • 68,093
  • 33
  • 169
  • 336
  • what difference does it make? `const_foo ()` is `const` either way – 463035818_is_not_an_ai Aug 02 '23 at 08:00
  • @463035818_is_not_an_ai, Above is a minimal example. In reality, I want to do something special in `operator-> const` if it's invoked. – iammilind Aug 02 '23 at 08:02
  • imho adding `std::cout << "this is a side effect"` would make the example more clear. – 463035818_is_not_an_ai Aug 02 '23 at 08:03
  • Rename `operator->` to explicit name? – Jarod42 Aug 02 '23 at 08:10
  • I do not think you can do that. Even with C++23 explicit this you will only be able to deduce const/non-const based on the object itself, but not the method this object calls. So you somehow must provide const object in order to call const overload. – Dmitry Aug 02 '23 at 08:16
  • @Jarod42, for now that's not in my radar. But I am still interested if you can come up with a solution with renaming. May be we can ultimately connect that with `opeartor->`. I hope you are not talking about macros like `INVOKE(y, foo)` – iammilind Aug 02 '23 at 08:17
  • 3
    If your `operator-> () const` does different things from `operator-> ()`, then you have a problem right here. It is like `const_cast` running some custom code. Don't do that. – n. m. could be an AI Aug 02 '23 at 08:20
  • https://eel.is/c++draft/over.match.oper#12 "The second operand of `operator->` is ignored in selecting an `operator->` function [...]" seems like it ruins your hopes (using that syntax) – Mat Aug 02 '23 at 08:22
  • @Mat, I am aware that mostly it's not possible, but since I couldn't find any similar question on SO, just wanted to ensure it. I may accept this if you can put it as an answer, unless someone comes up with a groundbreaking solution :-) – iammilind Aug 02 '23 at 08:25
  • I don't know the real use-case, but it may require to change the design because it seems impossible as written. This may also be an XY problem... (pun intended :D ) – Fareanor Aug 02 '23 at 09:05
  • Not a solution, I meant that having `X* as_X()`/`const X* as_const_X() const` might be a reasonable workaround. – Jarod42 Aug 02 '23 at 09:22
  • `const_cast(y)->const_foo();` – Eljay Aug 02 '23 at 11:37

1 Answers1

0

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.

Mestkon
  • 3,532
  • 7
  • 18