Home > Software engineering >  Multiple functions with the same name but their parameters are either constant or received by value
Multiple functions with the same name but their parameters are either constant or received by value

Time:11-05

The title is a bit lengthy, but it's best explained by an example:

Suppose we have the following functions in C :

void SomeFunction(int num) { //1

}

void SomeFunction(int& num) { //2

}
void SomeFunction(const int& num) { //3

}
void SomeFunction(const int num) { //4

}

All of these are called the same way:

SomeFunction(5);

or

int x=5;
SomeFunction(x);

When I tried to compile the code, it rightfully says

more than one instance of overloaded function "SomeFunction" matches the argument

My question is: Is there a way to tell the compiler which function I meant to call?

I asked my lecturer if it's possible, and she tried something along

SomeFunction<some text which I don't remember>(x);

But it didn't work and she asked me to find out and tell her.

I also encounter this post: How to define two functions with the same name and parameters, if one of them has a reference? And it seems that 1 and 2 can't be written together, but what about 3 and 4? Can either one of those be called specifically?

CodePudding user response:

1 and 4 have the same signature, so you'll need to drop one of those.

The other functions cannot be called directly, but you could add a template function that allows you to specify the desired parameter type:

template<class Arg>
void Call(void f(Arg), Arg arg)
{
    f(arg);
}

// Driver Program to test above functions
int main()
{
    int i;
    Call<int>(SomeFunction, 1);
    Call<int&>(SomeFunction, i);
    Call<const int&>(SomeFunction, 1);
}

Alternatively you could use a function pointer to choose the signature.

int i;
static_cast<void(*)(int)>(&SomeFunction)(1);
static_cast<void(*)(int&)>(&SomeFunction)(i);
static_cast<void(*)(const int&)>(&SomeFunction)(1);

It would be preferrable to avoid this scenario though and only define overloads for either references or the signature void SomeFunction(int).


Note:

SomeFunction<some text which I don't remember>(x);

only works for template functions and SomeFunction is not a template function, so this is not an option here.

CodePudding user response:

You can HACK it, and I mean it - it's not a good solution to your problem, by static casting your function explicitly:

    static_cast<void(*)(int)>(SomeFunction)(i);
    static_cast<void(*)(int&)>(SomeFunction)(i);
    static_cast<void(*)(const int&)>(SomeFunction)(i);

Demo It will work for first 3 overloads. 4th one is equivalent to 1st: quote from the standard [over.load]:

Parameter declarations that differ only in the presence or absence of const and/or volatile are equivalent. That is, the const and volatile type-specifiers for each parameter type are ignored when determining which function is being declared, defined, or called

and there is an example:

int f (int);
int f (const int); // redeclaration of f(int)

Also note that you cannot call 2nd overload with rvalue (temporary).

CodePudding user response:

The only way I see this working the way your lecturer tried is if SomeFunction is a template and these four overloads are specializations.

template<typename T>
void SomeFunction(T num);

template<>
void SomeFunction<int>(int num) {}

template<>
void SomeFunction<int&>(int& num) {}

template<>
void SomeFunction<const int&>(const int& num) {}

template<>
void SomeFunction<const int>(const int num) {}

Then you can call it as follows.

SomeFunction<int>(x);
SomeFunction<int&>(x);
SomeFunction<const int&>(x);
SomeFunction<const int>(x);

Demo

However, this is incredibly stupid in this context. There are a lot of things wrong with the original overloads.

In the 4th one, the const is completely useless from the caller's perspective, because you can call it the same way you can call the 1st, and the argument is a copy anyway. The const only makes it so the argument is constant inside the function. Moreover, the 1st and 4th overloads cannot both be defined at the same time: the const is actually ignored in the prototype and it leads to a redefinition.

The 3rd overload is also useless because a const int& argument provides no benefit over a int argument. In fact, the compiler probably optimizes that away. The only difference is in the scenario I describe at the end. Of course, if the argument type is more complex (not just int or some other fundamental type), it often makes sense.

The 2nd overload is the only one that can modify the variable you pass as argument. However, if the 1st (or 4th, since it's really the same) overload is present as well, you cannot call the 2nd directly because the call would be ambiguous. You could still call the 1st with an rvalue (basically a literal or an expression like std::move(x)).

If the 2nd and 3rd overloads are the only ones present, then there is no ambiguity and you can call the 2nd with non-const lvalues and the 3rd with const lvalues or rvalues.

Demo

  • Related