#include <iostream>
using namespace std;
void somefunc(int a)
{
cout<<"somefunc1";
}
void somefunc(int &b)
{
cout<<"somefunc2";
}
int main()
{
// case 1
somefunc(10); // works fine and prints somefunc1
//case2
int b=10;
somefunc(b); // generates compiler error that overloading is ambiguous
return 0;
}
In main() if I pass a constant (say 10) program compiles and runs and prints "somefunc1", but when I pass a variable (b in this case) compiler generates an error that overloading is ambiguous.
I don't understand how is it working internally.
Please help!!
The rules for overload resolution are a bit complicated. This is a simplification, applicable to this particular example.
The first step the compiler goes through is finding the "overload set", that is, the set of functions that can be called with the argument. For somefunc(10), only somefunc(int) can be called; somefunc(int&) cannot be called, because 10, being a constant, can't be passed by (non-const) reference. So there's only one function in the overload set, and that's the one that gets called.
For somefunc(b), both functions can be called. So the overload set has two functions, somefunc(int) and somefunc(int&). Now the compiler has to determine which function is the "best match" for the argument 10. And the rule is that int and int& both provide an "exact match", so the rules do not prefer one over the other, and the call is ambiguous.
If the second version of the function had been somefunc(const int&) instead of somefunc(int&) the call somefunc(10) would also be ambiguous.
Related
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 was 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?
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.
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).
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
How do we make (sure) C++ overload resolution will always resort to alias arg. than normal arg., so e.g. must pick
int func(string& s) {
// ...
}
over
int func(string s) { //...
}
when the argument valid to both?
thank you
If the argument is valid to both (eg std::string), then the code will not compile (GCC9.2) because of the ambiguity
error: call of overloaded 'func(std::string&)' is ambiguous
https://gcc.godbolt.org/z/qbvrAR
If you pass const char* or something else, which is default constructible to std::string, then the function, which takes copy of a string, will be called.
https://gcc.godbolt.org/z/DGSuEz
There is (not known to me) no way how to tell the compiler that he should prefer reference function over copy. You can check this snippet where I'm trying to explicitly call the reference, but compilation fails again due tu ambiguity:
https://gcc.godbolt.org/z/gZSdyj
I am a bit puzzled with the following behavior. I pass a function with two parameter, one having a default value, as a template parameter and call the function with one argument. Why does it fail to compile? And, what is the solution/workaround?
#include <iostream>
using namespace std;
template<typename Function>
void eval(Function function) {
function(10);
}
void sum(int i, int j = 0) {
cout << "Sum is " << i + j;
}
int main() {
sum(10); // OK, of course :)
eval(sum); // Error!
}
Note that this question is not about calling a templated function with default parameter.
The error message:
prog.cpp: In instantiation of 'void eval(Function) [with Function = void (*)(int, int)]':
prog.cpp:15:10: required from here
prog.cpp:6:10: error: too few arguments to function
function(10);
^
That's because the optional parameter is part of function declaration. When you call using a function pointer, essentially all the compiler knows is the type of the function pointer. So this line function(10) roughly translates to:
void (*)(int, int) sm = function; // the equivalent happens at the type deduction step
sm(10); // whoops, where is the second int
The compiler will need the second argument because it has no way of knowing whether sm is pointing to sum which has a default argument, or some other void foo(int a, int b) which doesn't have a default argument.
This is why function pointers are weak. You got two good answers of why this doesn't work - the function template doesn't know your function has a defaulted argument.
The solution is to not pass a function pointer - pass in a function object. In this case, you can handwrite a lambda:
eval([](int i){ return sum(i); });
which seems like a really annoyingly verbose way of basically just writing sum in a way that actually works. But this sort of thing comes up periodically (what if sum were an overloaded name?) so it's handy to just have a lambdafying macro around (C++14 required):
#define AS_LAMBDA(func) [&](auto&&... args) -> decltype(auto) { \
return func(std::forward<decltype(args)>(args)...); }
so you can write:
eval(AS_LAMBDA(sum));
That will work with defaulted arguments, overloaded names, and even work to bind member functions:
struct X {
int i;
int add(int j) { return i + j; }
};
X x{42};
eval(AS_LAMBDA(x.add));
When you call sum:
sum(10);
compiler translates it to:
sum(10, 0);
There is no error, as arguments are matched. For the compiler there exist no sum taking one argument. You can say a pre-compiler doing the magic of passing 0 to second argument to sum. With templates this magical pre-compiler doesn't exist, and the actual compiler matches sum with two strictly two arguments, which it doesn't find, and hence the error.
Just like
void foo(int);
void foo(float);
will fail to compile with foo(10.2); where argument double can be changed to either float or int. The compiler will not assume float for you. Similarly, compiler will not try to find best match of sum just for the sake of successful compilation.
When I'm passing function as parameter to other functions in c++ , do I have to specify it as
void callOtherFunctions(void f());
or
void callOtherFunctions(void (*f)());
I have no idea what happens under the hood , so I tried running both versions with a simple program as below , replacing the necessary part for 2nd run.
#include <iostream>
using namespace std;
void printHello();
void callOtherFunctions(void f());
int main() {
callOtherFunctions(printHello);
return 0;
}
void printHello(){
std::cout<<"\n Hello World";
}
void callOtherFunctions(void f()){
for (int i=0;i<5;++i){
f();
}
}
and to my surprise , both execute with same output and no warnings. So which is the preferred way , or correct way ( in case I'm doing something wrong ). And what actually happens in each case , when I pass pointer - does it executes address function there and when I pass function - does it copies down whole function there? or something else?
Here is Ideone Link
void callOtherFunctions(void f());
and
void callOtherFunctions(void (*f)());
are identical. Quoting N1570,
§6.7.6.3/8 A declaration of a parameter as "function returning type"
shall be adjusted to "pointer to function returning type", as in
6.3.2.1.
I would prefer the function pointer syntax because more people would be familiar with it and it's explicit what you mean. To answer your second question, a conversion also happens in certain cases (informally known as "decay"):
§6.3.2.1/4 A function designator is an expression that has function
type. Except when it is the operand of the sizeof operator, the
_Alignofoperator,65) or the unary & operator, a
function designator with type "function returning type" is converted to an expression that has type "pointer to function returning type".
Function parameter declarations are somewhat unusual; the compiler will adjust some of the declared types. This is one of them: function parameters of function type are adjusted to the corresponding pointer type.
Other common adjustments to function parameters are array to pointer type, and removing top-level const:
int foo(int a[5]); // a is a pointer
int foo(const int a); // foo() can be called with a non-const int argument.
Can i actually use a function overloading like this:
#include <iostream>
void foo(...)
{
std::cout << "::foo(...) \n";
}
void foo(int)
{
std::cout << "::foo(int) \n";
}
int main()
{
foo(0);
foo('A');
foo("str");
foo(0, 1);
}
What standard says about it? And in what kind of situations i'll get ::foo(...)?
void foo(int)
will accept one argument of type int.
void foo(...)
accepts any number of arguments, of any type. It will be selected when the call doesn't have a single int argument. Not very useful, in general.
Also note that it is undefined behavior to pass objects of class type to ....
In N3337 I can see:-
13.3.2 Viable functions
A candidate function having fewer than m parameters is viable only if
it has an ellipsis in its parameter list (8.3.5). For the purposes of
overload resolution, any argument for which there is no corresponding
parameter is considered to “match the ellipsis” (13.3.3.1.3) .
When you declare a function in the following way :
void foo (...)
this mean foo accepts any number of arguments.
So this function will be called when this is the must suitable one.
In your case whenever you won't write :
foo(//Some single int).
In your specific main, this will happen :
foo(0) //Calls foo(int).
foo('A) //Calls foo (int). as you can convert a char to an int.
foo("str") //Calls foo(...) . as you can not convert a string to an int.
foo(1,2) //Calls foo(...) . as this is the only possible function
cause the second foo function only takes one int.
void foo(...) will take variable arguments. And it will be called when there the no or type of argument does not match the provided argument list of other function with the same function name.
foo(0); //This will call void foo(int) function
foo('A'); //This will call void foo(int) function
foo("str"); //This will call void foo(...) function
foo(0, 1); //This will call void foo(...) function
NOTE:
Although the ellipsis works fine with function overloading, it is not highly recommend pursuing variadic functions. At least not until you have significantly more experience in C++ to understand the pitfalls. I would suggest its use only with the try catch block where there are situations when the error cannot be predicted.