Reputation: 2620
I am experimenting with the perfect forwarding feature of C++11. Gnu g++ compiler reports an ambiguity issue of function-parameter binding (the error is shown after the source code below). My question is why is it so, as following the function-parameter binding process I don't see the ambiguity. My reasoning is as follows: call to tf(a) in main() binds to tf(int&) since a is an lvalue. Then function tf forwards the lvalue reference int& a to function g hence the function void g(int &a) should be uniquely invoked. Thus I do not see the reason for ambiguity. The error disappears when the overloaded function g(int a) is removed from the code. This is strange as g(int a) cannot be a candidate for binding with int &a.
Here is my code:
void g(int &&a)
{
a+=30;
}
void g(int &a)
{
a+=10;
}
void g(int a) //existence of this function originates the ambiguity issue
{
a+=20;
}
template<typename T>
void tf(T&& a)
{
g(forward<T>(a));;
}
int main()
{
int a=5;
tf(a);
cout<<a<<endl;
}
Compilation g++ -std=c++11 perfectForwarding.cpp reports the following errors:
perfectForwarding.cpp: In instantiation of ‘void tf(T&&) [with T = int&]’:
perfectForwarding.cpp:35:7: required from here
perfectForwarding.cpp:24:3: error: call of overloaded ‘g(int&)’ is ambiguous
perfectForwarding.cpp:24:3: note: candidates are:
perfectForwarding.cpp:6:6: note: void g(int&&) <near match>
perfectForwarding.cpp:6:6: note: no known conversion for argument 1 from ‘int’ to ‘int&&’
perfectForwarding.cpp:11:6: note: void g(int&)
perfectForwarding.cpp:16:6: note: void g(int)
Upvotes: 3
Views: 618
Reputation: 20523
Adding on top of Jonathan Wakely's answer.
First of all, the issue has nothing to do with perfect forwarding and we can remove tf
from the picture.
For the time being consider just this code:
void g(int) {}
int main() {
int a = 5; // a is an lvalue
g(a); // ok
g(std::move(a)); // std::move(a) casts a to an rvalue and this call is also ok
}
This illustrates that a function that takes a parameter by value can take both lvalues and rvalues.
Now suppose we add
void g(int &) {}
then the first call, g(a);
, becomes ambigous because g(int &)
can take non-const
lvalues and nothing else. The second call, g(std::move(a))
is still ok and still calls g(int)
because g(int &)
can't take rvalues.
Now replace g(int &)
with g(int &&)
. The latter function can take non-const
rvalues only. Hence the call g(a)
is ok and calls g(int)
. However, g(std::move(a))
is now ambiguous.
At this point it becomes obvious that if we have the three overloads together, then the two calls become ambiguous. Actually, there's no reason for having the three overloads. Depending on the type T
, most often we have either
g(T)
org(T&)
org(const T&)
org(const T&)
and g(T&&)
.Upvotes: 4
Reputation: 171333
This is strange as g(int a) cannot be a candidate for binding with int &a.
That's not true. If you remove the g(int&)
overload then g(int)
will get called. When both are declared it is ambiguous, because both are viable candidates and require no conversions.
Upvotes: 7