Following is the test code:
struct A
{
operator int ();
operator int () const;
};
void foo (const int);
Now, upon invoking:
foo(A()); // calls A::operator int()
Why does it always chooses the non-const version ? Even making operator const int () const;
doesn't have any effect on invoking foo()
. Apart from standard reference, can someone explain logically, the reason behind it ?
There are legitimate uses of having two member functions with the same name with one const and the other not, such as the begin and end iterator functions, which return non-const iterators on non-const objects, and const iterators on const objects, but if it's casting from const to do something, it smells like fish.
If the function is non-constant, then the function is allowed to change values of the object on which it is being called. So the compiler doesn't allow to create this chance and prevent you to call a non-constant function on a constant object, as constant object means you cannot change anything of it anymore.
No. A reference is simply an alias for an existing object. const is enforced by the compiler; it simply checks that you don't attempt to modify the object through the reference r .
CPP. When a function is declared as const, it can be called on any type of object. Non-const functions can only be called by non-const objects.
A()
gives you a temporary A
object that is not const-qualified. The A()
expression is an rvalue expression, yes, but that does not make the A
object const-qualified.
Since the A
object is not const-qualified, the non-const operator int()
is an exact match and the const operator int()
requires a qualification conversion, so the non-const overload is selected as the best match.
If you want it to be const-qualified, you'd need to explicitly request a const-qualified A
:
foo(identity<const A>::type());
where identity
is defined as
template <typename T>
struct identity { typedef T type; };
Note that there is really no difference between operator const int() const
and operator int() const
: the result is an rvalue and only class-type rvalues can be const-qualified (int
is not a class type).
Note also that there is no difference between the void foo(const int)
that you have and void foo(int)
. Top-level const-qualifiers on parameter types do not affect the type of the function (i.e., the type of both of those declarations is void foo(int)
). Among other reasons, this is because it doesn't matter to the caller whether there is a top-level const-qualifier; it has to make a copy regardless. The top-level const-qualifier affects only the definition of the function.
James McNellis’ answer really covered it all, but it doesn’t hurt (I hope) with more explanations.
So.
When you call …
o.operator int()
… then the overload selection depends entirely on the constness of o
.
Nothing else.
To see why, consider this class:
struct Bar
{
void f() {}
void f() const {}
};
Technically those member functions do not need to be member functions. They could just as well have been chosen to be free standing functions. But then they need Bar
argument:
struct Bar
{};
void f( Bar& ) {}
void f( Bar const& ) {}
And hopefully now it's easier to see that when you do
Bar o;
f( o );
then the first function can be selected. And so it is. Because if the second function was selected, then you could never get the first one. Because if you make the object const
, then it would break const
correctness to select the first one. So when the object is const
only the second can be selected, hence, when it is not const
the first one is selected.
In short, the only practical alternative to this rule would be to always select the second one, which would make the first one rather useless, yes?
Cheers & hth.,
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With