kuk
kuk

Reputation: 127

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

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? :)

Upvotes: 2

Views: 88

Answers (3)

Daniel Trugman
Daniel Trugman

Reputation: 8501

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.

Upvotes: 4

user12002570
user12002570

Reputation: 1

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

Upvotes: 2

Jarod42
Jarod42

Reputation: 217663

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

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

Upvotes: 2

Related Questions