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:
- If you cast your user into a
const
one, you can call theconst
prototype. - 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();