I need some help understanding how overloaded C++ functions are selected by the compiler.
When given a function:
type func(type1 x, type2 y);
type func(type3 x, type2 y);
How does the compiler determine which function to choose?
I know the function chosen is according to its suitability, but how to you know which function is chosen if either could be used successfully.
For example:
Given these function overloaded functions:
char* average(int i, float f, double d);
double average(double d, float f, int i);
double fabs(double d);
Given these variables:
int i1, i2, i3;
float f1, f2, f3;
What data type is the return value of these function calls? and why?
average(i1, i2, i3);
average(f1, f2, f3);
fabs(average(f1,f2,f3));
The return value depends on the function call that is made. For example your first function call would return value double because second average function is called.
The function overloading solely takes place on basis of arguments irrespective of return types. You can't overload two functions solely on basis that they have different return types. The functions should differ by arguments in order for function overloading to work.
In order to compile a function call, the compiler must first perform name lookup, which, for functions, may involve argument-dependent lookup(ADL).
If these steps produce more than one candidate function, then overload resolution is performed to select the function that will actually be called.
In general, the candidate function whose parameters match the arguments most closely is the one that is called.
As in your case:
1--> char* average(int i, float f, double d);
2--> double average(double d, float f, int i);
Here in both of your functions you have completly different argument list. 1st average is taking int, float, double and the 2nd one is double, float, int.
So, compiler decides which method to invoke first based in its arguments.
If you invoke method with argument (int, float, double) the overloaded version takes 1st method and if you invoke method with argument (double, float, int) the overloaded version takes 2nd method.
The choice if which overloaded method to call(in other words, the signature of the method) is decided at compile time.
So, compiler will already know the signature of the method to be invoked.
While overloading methods we must keep the following rules in mind:
Overloaded methods MUST change the argument list,
Overloaded methods CAN change the return types.
Overloaded methods CAN change the access modifier.
A method can be overloaded in the same class or in a subclass.
Related
Please refer to the following:
struct functorOverloaded
{
void operator()(const int& in_, ...) const {}
void operator()(short in_) {}
};
// helper to resolve pointer to overloaded function
template <typename C, typename... OverloadArgs>
auto resolve_overload(
std::invoke_result_t<C, OverloadArgs...> (C::* func)(OverloadArgs..., ...) const
)
{ return func; };
int main(int argc, char **argv)
{
using C = const functorOverloaded;
// works with exact function type
using myT = decltype(resolve_overload<C, const int&>(&C::operator()));
// can call with something convertible to const int&
static_assert(std::is_invocable_v<C,int>, "!!!");
// how to get the pointer to the overload that would be called when passed int (or double)?
// the next line doesn't compile (error C2672: 'resolve_overload': no matching overloaded function found)
using myT2 = decltype(resolve_overload<C, int>(&C::operator()));
return 0;
}
The above code allows retrieving a pointer to a specific overload of a function (operator() in this case), see here. One must know the exact argument type (const int&) in this case to get the pointer, even though i can just call the specific overload with a plain int, or even double. Is it possible to get a pointer to the overload that would be called with the specific argument (assuming the call is resolvable / not ambiguous)?
Edit: adding context:
I am writing a invocable_traits library for introspecting a callable. E.g., given a Callable, it will tell you the return type, arity and argument types, amongst some other properties. To support functors (including lambdas) with overloaded (or templated) operator(), the API of invocable_traits allows specifying call arguments to disambiguate which overload is to be used (or to instantiate the template). However, one must know the exact argument type (const int& in the example above), simply specifying int won't do in that case as there is no function with signature R operator()(int). Ideally, I'd like to allow discovering the signature of the exact overload/instantiation that gets called given the provided input argument types, ideally even taking into account any implicit conversions that are applied. Is this possible?
There is no way to get the function of an overload-set which would be called with the given arguments, unless you already know its signature.
And if you know, what's the point?
The problem is that for any given arguments, taking into account implicit conversions, references, cv-qualifiers, noexcept, old-style vararg, default arguments, and maybe also literal 0 being a null pointer constant, there are an infinite number of function-signatures which would match. And there is currently no facility for "just" listing all candidates.
I have this example about std::function:
int add(int x, int y, int z) {return x + y + z;}
int add(int a, int b) {return a + b;}
int main()
{
std::function<int(int, int)> fn = add; // error
int(*pfn)(int, int) = add; // OK
fn = pfn; // ok fn is bound to add(int, int)
std::cout << fn(5, 7) << std::endl; // 12
}
Why Overload resolution doesn't resolve which version of add when initializing fn but is able to initialize the function pointer pfn?
Is there a workaround rather than using function pointer to decide which version of an overloaded function as an initializer to an std::function object?
Why Overload resolution doesn't resolve which version of add when initializing fn but is able to initialize the function pointer pfn?
Because overload resolution is performed in the initialization of function pointer (like pfn), based on the type of the function pointer.
In all these contexts, the function selected from the overload set is the function whose type matches the pointer to function, reference to function, or pointer to member function type that is expected by target: the object or reference being initialized, the left-hand side of the assignment, function or operator parameter, the return type of a function, the target type of a cast, or the type of the template parameter, respectively.
On the other hand, such overload resolution doesn't happen when initializing a std::function, which has a constructor template and the template parameter needs to be deduced from the function argument; the compiler can't choose one for the deduction.
As the workaround, you can apply static_cast to specify the overload you want explicitly.
static_cast may also be used to disambiguate function overloads by performing a function-to-pointer conversion to specific type
std::function<int(int, int)> fn = static_cast<int(*)(int, int)>(add);
For the function pointer case, C++ has a special rule that permits a sort of "time travelling" lookup. It can perform overload resolution on a name, based on what you're going to assign/initialise the name to.
This is basically a hack built into the language (rules in [over.over]).
No other part of the language works this way. For example, where newcomers often expect that after writing float x = 1/2 the value of x will be 0.5, we have to explain that the fact you're initialising a float has no relevance to the types or calculation of the expression 1/2.
This hack was not extended to std::function. Presumably that is because adding hacks is bad, and because it is not needed for this case. Why not? Because you can still deploy the hack indirectly with a static_cast on the RHS of your std::function initialisation:
std::function<int(int, int)> fn = static_cast<int(*)(int, int)>(add);
… and there's your workaround.
The error is (using VS2013 - GLM):
more than one instance of overloaded function "glm::slerp" matches the
argument list: function template "glm::detail::tquat
glm::slerp(const glm::detail::tquat &x, const glm::detail::tquat
&y, const T &a)" function template "T glm::slerp(const
glm::detail::tquat &x, const glm::detail::tquat &y, const T &a)"
argument types are: (glm::quat, glm::quat, float)
I'm calling it with two glm::quat and a constant float value, just like you would do with glm::lerp and glm::mix. The two overloaded functions shown in the error message only differs on the return type. I've been searching for an answer without any luck.
The exact call:
const float t = 0.5f;
glm::quat newQ = glm::slerp(quatA, quatB, t);
It is impossible (hear illegal) in C++ to have function definitions that differs only by return type because the return type is not part of the signature, and therefore will violate one definition rule.
I suggest you write the template parameters explicitely, or cast to something you are sure ?
I have a function that is defined as follows:
template < class T> T doSomething(const T value, const T value2, const T value3)
{
T temp = value;
//Do stuff
return temp ;
}
in my main, I go to call it as follows:
doSomething(12.0, 23.0f, 2.0f);
I get an error saying no matching function for call doSomething(double, float, float).
I tried to used const_cast but it didn't seem to fix the issue. Any help would be appreciated.
It's not about the const, but about that T cannot be both double and float at the same time.
If you have a non-template function, one or more parameter can be promoted (or converted) to the parameter type. With a template function, the compiler will have to start by determining what the template type is supposed to be. Here it cannot decide.
Your function definition uses same type "T" for every of three arguments.
C++ is not able to deduct types in cases like this.
Please choose way to fix:
Different types for every argument
template<typename A, typename B, typename C>
A doSomething(const A& value, const B& value2, const C& value3)
{
A temp = value;
//Do stuff
return temp ;
}
Explicit template argument on call:
doSomething<int>(12.0, 23.0f, 2.0f);
Explicit type cast for argument on call:
doSomething(12.0, static_cast<double>(23.0f), static_cast<double>(2.0f));
When using a templated function, you need to supply the template parameters within angle brackets.
I.e. you need to use doSomething<float>(12.0, 23.0f, 2.0f);
Or, at least, if you don't, the compiler has to guess what T is, which means when it sees a double for the first argument it assumes you mean doSomething<double>()
When you call like this:
doSomething(12.0, 23.0f, 2.0f);
then you basically let the compiler to deduce the template argument T, from the function arguments. But in the above call, type of all arguments are not same : the first argument12.0 is double, while the rest two arguments are float (Note that f in 2.0f makes it a float, while 2.0 is a double.). Hence the problem, as the compiler is trying to find a function which accepts double, float, float as arguments. But there is no such function. And the function template cannot be deduced from the arguments, because T has to be same for all arguments.
So the solution is, let T be deduced as either double or float:
doSomething(12.0f, 23.0f, 2.0f); //all are float now
Or this:
doSomething(12.0, 23.0, 2.0); //all are double now
In the first case, T is deduced as float, and in the second case, T is deduced as double.
Alternately, you may choose not to depend on template argument deduction, by providing template argument as:
doSomething<float>(12.0, 23.0f, 2.0f);
Or this:
doSomething<double>(12.0, 23.0f, 2.0f);
Here, in the first case, the 1st argument converts into float, and in the second case, 2nd and 3rd argument convert into double.
I am a beginner in C++. I wanted to know, how internally the function overloading takes place in C++
I recommend you reading C++ Primer. This concept is explained in depth.
On a higher level, function overloading is allowed if both the functions
have same name
declared in the same scope
have different parameter list.
On the lower-level ( inside how compiler figures it out), here's how it is done.
There are 3 steps for function overloading resolution.
e.g void f();
void f(int);
void f(double, double = 3.4);
void f(char *, char *);
Function call inside main -> void f(5.6);
Identifies the set of overloaded functions considered for the call, they are called as candidate functions. A candidate function is a function with the same name as the function that is called & for which declaration is visible at the point of call.
It also identifies the properties of the argument list in the function call, i.e no of arguments and their types.
Selected : All 4 functions with name 'f'
Selects the function from the set of candidate functions found in step 1 that can be called with arguments specified in the call. Those are called as viable functions.
A viable function is a function that has the same nof of parameters or more parameters ( addn paramters has an associated default argument) than the arguments in the actual function call. Types of arguments must be convertible for the function to be classified as viable.
Selected : void f(int) and void (double, double =3.4)
Identifies the best viable function among the all viable functions.
For (int) conversion need to apply which is a standard conversion (double to int). But for (double, double=3.4), there's a exact match, so no conversion needed. No conversion is better than a conversion.
Selected : void (double, double = 3.4 )
It takes place internally with mangled names.
For the file
void blah(int f)
{
}
void blah(double f)
{}
I get the symbols
ethan#EthanPc ~ % nm blah.o
0000000000000009 T _Z4blahd
0000000000000000 T _Z4blahi
Notice the d for double, and the i for integer.