Functions With Const Arguments and Overloading

functions with const arguments Overloading ( Follow up)

In C++, the function signatures

int Test::foo (const int a) const

and

int Test::foo (int a) const

are considered to be complete identical.

The reason that the const on the parameter is disregarded is because it can not affect the caller in any way. As the parameter is passed by value, a copy is made of the value provided by the caller. To the caller, it does not matter in any way if the called function can change that copy or not.
For this reason, C++ ignores a top-level const-qualification on function parameters (top-level const can not occur if passing a reference), and goes even as far that int foo(int); is considered a correct prototype for the function

int foo(const int)
{
/* ... */
}

In short, it is impossible in C++ to overload a function on the constness of (value) function parameters.
To get the output you want, you could consider using a non-const reference parameter for your non-const overload.

const Function Overloading

const char* and char * are actually not the same. The later allow for modifying the pointed char, while the first one will prevent that.

Also note that if those were class methods, void display() and void display() const would also be valid overloads. The later would imply that the method must not change the object's state.

Consider this code:

void    display(char *s)
{
std::cout << "Display" << std::endl;
}

void display(const char *s)
{
std::cout << "Display with const" << std::endl;
}

int main()
{
char *str = strdup("boap");
const char *str2 = "toto";
/* It is a string literral "bound" as a char *.
Compiler will issue warning, but it still compiles.
Avoid to do that, it's just an exemple */
char *not_safe = "not_safe";

display("llama");
display(str2);
display(str);
display(not_safe);
}

This will print Display with const twice, and then twice Display. See there.
Now, let's see why:

  • "llama" is a string literal, and then is resolved as a const char *.
  • str2 is a pointer to a string literal. Since its type is const char*, this also revolves to the const overload.
  • not_safe is also a pointer to a string literal. However, its type is char *: this is not correct. The memory it points to is read-only, and trying to modifies it will result in a crash. However, the type of the variable is still char *, so this resolve to the non-const overload.
  • str is a char * pointer, and the string it points to is not read-only. Modifying its content is valid, and since its type is char *, it will resolve to the non-const overload.

Why are const qualifiers in function arguments used for overloading resolution?

I thought the const was only there to tell the compiler that the object being
passed is not changed and in the second case it is copied anyway

You are correct. Because in the second case it's copied anyway, and so the const makes no difference to the caller, the standard defines that void f(const int x) and void f(int x) have the same signature. Hence they collide, you're trying to define the same function twice.

Because in the first case it isn't copied anyway, void f(const int &x) and void f(int &x) have different signatures. Hence they overload.

In your first case, it's forbidden to call the int& version of f with x2 as argument, because that would create a non-const reference to a const object without any explicit cast in sight. Doing that defeats the purpose of the const system (which is that if you want to break const-safety, you have to do so explicitly with a cast). So it makes sense to have const- and non-const overloads of functions with reference parameters.

In your second case, there's no relation between the const-ness of the source of a copy, and the const-ness of the destination. You can initialize a const variable from a non-const one, or a non-const one from a const one. This causes no problems and doesn't break const-safety. That's why the standard helpfully makes this clear by defining that your two "different" versions of f are actually the same function.

How does overloading of const and non-const functions work?

In the example you gave:

vector<int>::const_iterator it = myvector.begin();

if myvector isn't const the non-const version of begin() will be called and you will be relying on an implicit conversion from iterator to const_iterator.

Overloading with “const” at end of function declaration

Member functions have an implicit argument which is the instance of the class itself. So you can think your functions as really looking like:

// 1) compile-error as const-qualifications on return doesn't distinguish
// the functions - you cannot overload on return type
int begin(Test& );
const int begin(Test& );

// 2)
int begin(Test& );
const int begin(const Test& );

// 3)
int begin(Test& );
int begin(const Test& );

With the 2nd and 3rd cases, the const-qualification on the function is equivalent to that implicit argument being a reference-to-const. So when you have something like:

Test{}.begin();

That calls begin() using a reference to non-const Test as the implicit first argument. Both overloads of begin() are viable, both require no conversions, so the least cv-qualified reference is preferred, which is the non-const-qualified function.

Instead, when you have:

(const Test{}).begin();

we're calling begin() with a reference to const Test. So the non-const-qualified function is not a viable candidate (you cannot pass a const-ref to a function expecting a non-const-ref), so the best viable candidate is the only viable candidate: int begin() const.



Related Topics



Leave a reply



Submit