operator[] ambiguity resolution - c++

noob here. The following is a fragment from a class definition I came accross in a book example:
double& operator[](int i);
double operator[](int i) const;
My question is: why is this not ambiguous? When compiling the files of the project, the compiler doesn't give any error.
Also, in the following (imagine AnyClass contains a valarray<double> object for example and I want to access it directly):
AnyClass test;
cout << test[2]
which version does the compiler use?

It's not ambiguous because the const is part of the signature and can be used for overload resolution. So if you use operator[] on a non-const object, it picks the overload without const because that's the most specific one. And if you use it on a const object, it picks the overload with const because that's the only one that applies.

If called on a const object, the const version will be used, else the other one.
That's how the compiler resolves the ambiguity.

AnyClass test;
const AnyClass const_test;
std::cout << test[2]; // calls operator[](int)
std::cout << const_test[2]; // calls operator[](int) const

To understand this, you mostly simply need to realize that a const on an argument is enough to disambiguate a call:
#include <iostream>
void foo(char* ptr)
{
std::cout << "mutable: " << ptr << std::endl;
}
void foo(const char* ptr)
{
std::cout << "const: " << ptr << std::endl;
}
int main()
{
const char* constHello = "hello";
char mutableHello[] = "hello";
foo(constHello);
foo(mutableHello);
}
This prints:
const: hello
mutable:hello
The compiler will choose the least restrictive overload it can. So if you use a char* when there's a char* overload, it's the one it will pick; but if there isn't any, the compiler will decide that casting it to a const char* is a viable conversion (the converse is, obviously, not true).
Now, the very simple thing is that all methods pass a this pointer as the first parameter of any function. This parameter is hidden for the sake of simplicity. The const at the end of the method qualifies the this argument. Since, as we've just seen, a const on a pointer is enough to disambiguate overloads, this will effectively work.

Related

operator & overload and indexing in C++

Is it possible to create an "address of" (&) operator overload that would allow passing an arg? So kind of a mixture between overloading the operator [] and operator &.
So regular [] overload can be used like this
val = myobj[arg];
where:
SomeReturnType MyClass::operator[](SomeArgType i)
{
// Code to handle indexing
}
But if we want a pointer to that item, what I'm looking for would provide this use:
ptr = &myobj[arg];
Maybe this isn't possible directly, and I know there's other ways to achieve this, but if it is possible I do not know the syntax.
You do not need to overload adress of operator when operator[] returns a reference:
#include <iostream>
struct foo {
int x;
int& operator[](int){
return x;
}
};
int main() {
foo f;
std::cout << &f.x << "\n";
std::cout << &f[42] << "\n";
}
Possible output:
0x7ffc8cd2d46c
0x7ffc8cd2d46c
The example uses only a single int, but it will be the same when operator[] is returning reference to an element of some container.
Returning a copy from operator[] and then get the adress of the original element is not possible via operator& overload (unless you return some proxy that holds both, the copy of the element and the adress of the original, from operator[]).

Why is the method with const used instead of the first method which has no const

void print( int & a ){ cout << a << endl; }
void print( const int & a ){ cout << 2*a << endl; }
void print( float a ){ cout << 3*a << endl; }
Why is 2 the output of print(1)?
The tenet of this question is that you need to know that anonymous temporaries cannot bind to non-const references, and they can bind to const references.
1 is an anonymous temporary of type int.
Hence the const int& binding is favoured by overload resolution.
Some compilers (older MSVC) would allow, in error, a binding to int&. In standard C++, if the const int& overload is missing, then the float overload will be called.
For all three overloads:
(void print( int & a ){ cout << a << endl; }) The first overload is not called, since it only accepts a variable. (Unless you have an old MSVC compiler).
(void print( const int & a )) The second overload is of type const int&, which can accept either a temporary of type int, or a variable of type int.
(void print( float a )) The third overload has an argument of type float, which would work be called if there was no overload of type int.
Thus, since there is an overload that accepts a temporary variable of type int, the second overload is chosen over the third.

binding error in l-value reference and r-value reference

void f(int & i){
cout << "l-value-ref" << endl;
}
void f(int && i){
cout << "r-value-ref" << endl;
}
Assuming the code above, we have a overloaded function which takes respectively l-value-reference and r-value-reference parameters.
int x = 5;
f(x);
f(5);
const int j = 9;
f(j);
when i use const int j = 9 compiler gives ambiguity error. How can i solve this problem?
Your compiler error (if it is indeed the one you mentioned) is misleading. What is really wrong here is that attempting to pass a const int argument to any of your functions would discard the const qualifier, which is not allowed. You can solve it by either changing the signature to const int& / const int&& (note that const int&& will still not work with a const int argument), or by adding an another overload for const arguments, depending on what exactly do you want to achieve.
Basically the choice you have to make is between "I need one version that needs to be able to modify the passed reference" (2 versions then) and "I will never modify the passed reference anyway" (just the const int& version).

Overloading typecasting operators: const & non-const

Why do the following two usecases behave differently?
An example for a built-in type:
class Test
{
operator const char*() {
return "operator const char*() called";
}
operator char*() {
return const_cast<char*> ("operator char*() called");
// a really ugly hack for demonstration purposes
}
};
Test test;
const char *c_ch = test; // -> operator const char*() called
char *ch = test; // -> operator char*() called
Okay, everything works as it should. No, let's try it with a user-defined type:
class ColorWrapper
{
operator const Color() {
cout << "operator const Color() called" << endl;
return Color(10, 20, 30);
}
operator Color() {
cout << "operator Color() called" << endl;
return Color(11, 22, 33);
}
};
ColorWrapper cw;
const Color c_col = cw;
Color col = cw;
The situation looks identical; but now, GCC starts complaining:
error: conversion from 'ColorWrapper' to 'const Color' is ambiguous
error: conversion from 'ColorWrapper' to 'Color' is ambiguous
Am I missing something? What I'm trying to achieve here is to prevent the user from changing the color directly. If they want to change it, they must do it via the wrapper class.
The thing is, that your two examples are not equivalent. The first returns two different types from which the compiler can easily choose, a pointer to mutable data and a pointer to constant data. It would be different, was it a pointer vs a constant pointer (which would mean you cannot change where the returned pointer points, in contrast to not changing the pointed to data):
class Test
{
operator char * const();
operator char *();
};
which would be equivalent to your second example and introduce the same ambiguity, since the compiler cannot decide which one to choose, as both return the same type, one const-qualified and the other not.
This could be solved by overloading based on the constness of the object:
class ColorWrapper
{
operator const Color() const;
operator Color();
};
But this won't buy you anything here, since you return a new object by value anyway, so you can always return the const version, and also the appropriate version would be chosen based on the source object's constness and not the destination obejct's constness (thus call the non-const version in both cases), which doesn't seem what you want.
So you can overload based on the constness of the source object. But you cannot, as you want to now, overload simply based on the constness of the destination object.
This all reduces down to thinking about the difference between an object and the data the object may inderectly refer to, which in practice presents itself as the difference between a constant pointer and a pointer to constant data.
What I'm trying to achieve here is to prevent the user from changing
the color directly
Then there's absolutely no need for any overloading. Just return the const version. The user cannot change the returned color directly, he can only copy it and change the copy.
cw.set(red); //nope, return color is constant
Color col = cw; //copy returned color into col
col.set(green); //fine, since we're not working on the returned color, but on col
const Color c_col = cw; //use this if you don't want c_col to be modified,
//doesn't have anything to do with returned color, though
That is because in your second example you were adding const to a object , not pointer(in your first case). And const with object make compile confuse in overload resolution stage, those two are as good as each other(object vs const object) according to c++ standard. compiler won't be try to smart here and choose one over another.
But in pointer vs const pointer (reference works too), compile can choose from those two overload candiate accordingly,which is your first case.
If you modify one operator to be const, it will work:
operator const Color() const {
cout << "operator const Color() const called" << endl;
return Color(10, 20, 30);
}
Alternatively, you can remove one of the two operators. Since you return the Color by value, there's no real difference between the two operators.
You observe that problem because you basically have 2 different cases here. I think that the easiest way to present what is happening here is to modify a bit your first example:
class Test
{
operator char* const() {
return "operator char* () called";
}
operator char*() {
return const_cast<char*> ("operator char*() called");
// a really ugly hack for demonstration purposes
}
};
Now you have exactly the same situation as in the second example. You basically return a copy (of a pointer) and a const copy and that is why they mess up in overload resolution.
Your original first example works fine because those conversion operators convert to pointers of 2 different types (const char and char). I hope that helps to understand the difference here.
BTW, I personally believe that there is no much sense in returning a const copy from a method. It does not change anything to the user and only obfuscates the class interface.

function overloading with const parameters

Function overloading can happen between two member functions which have the same number of parameters, if one of them is declared as const.
But what if one function has a const argument, another has non-const argument of same type?
Will it work for references and pointers? If C++ provides it, why does it provide? Please share the reason with me if you know.
Below is the example that helps you in understanding the above scenario.
void fun(const int i)
{
cout << "fun(const int) called ";
}
void fun(int i)
{
cout << "fun(int ) called " ;
}
int main()
{
const int i = 10;
fun(i);
return 0;
}
Output: Compiler Error: redefinition of 'void fun(int)'
void fun(char *a)
{
cout<<"non-const fun() called";
}
void fun(const char *a)
{
cout<<"const fun() called";
}
int main()
{
const char *ptr = "GeeksforGeeks";
fun(ptr);
return 0;
}
Output: const fun() called
Why is the second one allowed in C++?
The first one's parameters are top-level const. This means that the function can't change the parameter's value, however, the caller doesn't care: The callee gets a copy of the argument, so if a parameter has top-level const, it's an implementation detail. Note that the following works:
void f(int); // forward declare
void g(){ f(42); }
void f(int const i){ /*...*/ } // define above declared function
For the second set of overloads, the const isn't top-level anymore. It describes whether or not the callee can change what the pointer points at. As a caller, you do care about that. It's not just an implementation detail anymore.
First, explain why the first code is not allowed while the second one is ok.
const int and int as parameter, you pass any related type, double, int or anything else can convert to int, both const int and int can accept the pass-in value, there's no difference practically. And if the complier allow to the define both, then which one to call? You don't know, neither the complier. So the first part of code is not allowed.
When it comes to second example, reference and pointer makes a difference. Because you can't pass a const int* to initialize int * and neither can use const int to initialize int&. So if you define two functions with same return type, one is "const version" pointer or reference parameter, and the other is not, that makes a difference. Another question comes up, what if I pass a int object(or called variable, same meaning) or int * pointer, then which one is matched (when parameters are pointer or reference)? The answer is the "non-const" one. if you want to match the "const version" with non-const object or non point to const pointer, you may need const_cast which I am trying to figure out.
So back to your question:
But what if one function has a const argument, another has non-const argument of same type? Will it work for references and pointers?
Yes, it to some extent only works for reference and pointers.
And
If C++ provides it, why does it provide?
Can't tell. I don't have much experience.
For further information, read the very related part sections of C++ Primer 5th.
Links of screenshots are listed as follows:
https://imgur.com/tnqrxVY
https://imgur.com/hF1MjUH
https://imgur.com/Fg2zeEw
By the way, though I am a newbie. But what is int const i from the first answer? And I don't understand what "it's an implementation detail" exactly mean. No offense, just can't understand that part of answer. :D