Home > OS >  How to call overloaded function based on constness and return type?
How to call overloaded function based on constness and return type?

Time:06-13

A rookie question here. I have noticed something similar with what I am trying to achieve in the cryptic vector class:

iterator begin();
const_iterator begin() const;

I have tried to achieve this with the following implementation:

#include <iostream>

class User
{
public:
  class A
  {
  public:
    A(){ std::cout << "A constructor" << std::endl; }
  };

  class B
  {
  public:
    B(){ std::cout << "B constructor" << std::endl;}
  };
public:
  A begin() { return A(); }
  B begin() const { return B(); }
};

int main()
{
  User u;

  User::A a = u.begin();
  User::B b = u.begin();
}

With this code, I can call the A constructor yet cannot find a way how to call the B constructor.

I get the following error message:

no user-defined conversion from User::A to User::B

Which, I guess, is indicative that the wrong member function is called.

Any tips? :)

CodePudding user response:

Before solving the problem, I'll add a small comment so that you understand why C behaves that way.

Basically, const functions are there so that you could expose different APIs when you are accessing mutable vs immutable object instances.

So, for example, you might have define an API that returns a list of const User-s that only allow calling specific getter methods, whereas a different API would return a single (non-const) User for editing.

The compiler automatically uses the right method according to the const-ness of the instance you are using.

So, if you have a const User a and User b, for user a, the default is going to be the const prototype, and for user b, the default is going to be the non-const one, and it falls back to the const one if none exists.

Note: As Goswin correctly pointed out, is that the compiler has no way of choosing a method by its return type, but rather only by examining its arguments. C methods, as in, functions attached to class instances, pass an "implicit" pointer to the class instance, called this. When defining a const method, you're basically telling the compiler to pass a const pointer as opposed to a regular one for non-const ones.

Just like others pointed out:

  1. If you cast your user into a const one, you can call the const prototype.
  2. OR, if you define or return a const User variable, you'll also access the const prototype as well.

There are different ways to cast:

  • C way (const User&)u (which you should avoid, but be able to recognize)
  • Classic const_cast<const User&>(u)
  • C 17 std::as_const(u)

And you can always return a const reference to an object from a method if you wanna expose only the const prototypes.

CodePudding user response:

You need const User, std::as_const (C 17) might help:

User::B b = std::as_const(u).begin();

CodePudding user response:

The problem is that the second overloaded begin is a const member function which means that when you write u.begin() the first non-const version will be preferred over the second const version since u is a nonconst User object.

To solve this one option is to use const_cast as shown below:

User::B b = const_cast<const User&>(u).begin();

Demo

  • Related