Reputation: 317
I was trying out default argument values and function overloading in c++ by compiling the following code and I was surprised by the output which was :
Line 19: error: call of overloaded 'add()' is ambiguous
The code I compiled is :
#include <iostream>
using namespace std;
void add(int a=1, int b=1){
cout<<a+b;
}
void add(){
int a =2, b=2;
cout<<a+b;
}
int main(){
add();
return 0;
}
Any explanations why it is ambiguous? Thx in advance.
Upvotes: 1
Views: 2440
Reputation: 77400
Overload resolution is defined by § 13.3 of the C++ standard(s) (at least C++03 and C++11). There are three parts:
Since add
names a function (rather than an object), § 13.3.1.1.1 defines how to determine candidate functions. Since add
isn't qualified (contains no .
or ->
operator), clause 3 applies (taken from draft n3337 of C++11):
In unqualified function calls, the name is not qualified by an -> or . operator and has the more general form of a primary-expression. The name is looked up in the context of the function call following the normal rules for name lookup in function calls (3.4). The function declarations found by that lookup constitute the set of candidate functions. Because of the rules for name lookup, the set of candidate functions consists (1) entirely of non-member functions or (2) entirely of member functions of some class T. In case (1), the argument list is the same as the expression-list in the call. [...]
In short, the candidate functions are those found by standard name lookup in the context of the function call. Name lookup is defined in § 3.4. Often, § 3.4.2 (argument-dependent name lookup) will find additional candidate functions, but there are no arguments in the function call in question, so only § 3.4.1 matters. In particular, clause 6:
A name used in the definition of a function following the function’s declarator-id that is a member of namespace N (where, only for the purpose of exposition, N could represent the global scope) shall be declared before its use in the block in which it is used or in one of its enclosing blocks (6.3) or, shall be declared before its use in namespace N or, if N is a nested namespace, shall be declared before its use in one of N’s enclosing namespaces.
In short, the current namespace and any parent namespaces are searched, and only functions already declared are considered. In the sample code, any functions declared before main
in the global namespace with a name of add
are candidates: add(int, int)
and add()
. If you were to declare (e.g.) a function add(float, float)
after main
, it wouldn't be a candidate function.
§ 13.3.2:
2 First, to be a viable function, a candidate function shall have enough parameters to agree in number with the arguments in the list.3 Second, for F to be a viable function, there shall exist for each argument an implicit conversion sequence (13.3.3.1) that converts that argument to the corresponding parameter of F. If the parameter has reference type, the implicit conversion sequence includes the operation of binding the reference, and the fact that an lvalue reference to non-const cannot be bound to an rvalue and that an rvalue reference cannot be bound to an lvalue can affect the viability of the function (see 13.3.3.1.4).
- If there are m arguments in the list, all candidate functions having exactly m parameters are viable.
- 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).
- A candidate function having more than m parameters is viable only if the (m+1)-st parameter has a default argument (8.3.6). For the purposes of overload resolution, the parameter list is truncated on the right, so that there are exactly m parameters.
The argument list has 0 arguments. add()
has 0 arguments, so it's viable. add(int, int)
has 2 arguments, but the first has a default argument, so it's viable. Since there are no arguments in the call, the conversion in clause 3 doesn't come into play, but it's important to be aware of the clause, especially as it points out that a function declared as int foo(int&)
can't be bound to a function call foo(0)
, since a non-const reference (e.g. int&
) can't be bound to an rvalue (e.g. a literal 0). However, int foo(const int&)
can be bound to foo(0)
.
§ 13.3.3 defines how one function is considered "better" than another when it comes to name resolution in terms of a partial ordering of functions:
As there are no arguments, criteria 1 can't be used. Neither add()
nor add(int,int)
is a template, so neither 2 nor 3 can be used. In short, neither function is better than the other.
Lastly, § 13.3.3 2 determines the final result:
If there is exactly one viable function that is a better function than all other viable functions, then it is the one selected by overload resolution; otherwise the call is ill-formed.
Since there are two viable functions in the sample code, the call is ill-formed.
Upvotes: 2
Reputation: 2661
void add(int a, int b);
void add();
You should not give a
and b
default values. With default values there is no way for the compiler to know whether a call to add()
should use the first or second function.
Is there a reason you need to give a
and b
default values?
Upvotes: 2
Reputation: 258548
Because both signatures match the call.
add();
can be interpreted as either add(1,1)
or add()
. When you write void add(int a=1, int b=1)
, you're telling the compiler - "Listen dude, if I call add
with no parameters, I want you to default them to 1
"
Most importantly, what do YOU expect to happen when you call add()
with no parameters?
If you expect it to print 2
, remove the version that takes no parameters.
If you expect it to print 4
, remove the default parameters from the first version.
Upvotes: 8