Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does the compiler not give an ambiguous reference error?

Tags:

With reference to the following code

#include <iostream> #include <tuple> #include <string> #include <type_traits>  using std::cout; using std::endl; using std::string;  template <typename... Args> void bar(Args&&...) {}  int change(const string&) { return 1; } double change(int) { return 1.0; }  int main() {     // bar(1, 2.0, static_cast<int(*)(const string&)>(&change));     bar(1, 2.0, &change);     return 0; } 

I understand that the error in the above code is that the reference to the change function is ambiguous (which is why the commented line works), but then why does the compiler give this error message?

test.cpp:17:5: error: no matching function for call to 'bar'     bar(1, 2.0, &change);     ^~~ test.cpp:11:6: note: candidate function not viable: requires 2 arguments, but 3 were       provided void bar(Args&&...) {}      ^ 1 error generated. 

This happens both on gcc (>5) as well as clang (Apple LLVM version 8.0.0 (clang-800.0.42.1))

I am just curious as to why both the compilers do not just say that the reference is ambiguous. I feel like it has something to do with how template instantiations work in C++ but I am not sure of the exact reason.

like image 813
Curious Avatar asked Jun 07 '17 17:06

Curious


People also ask

What is ambiguity error?

Ambiguity errors occur when erasure causes two seemingly distinct generic declarations to resolve to the same erased type, causing a conflict.

Is ambiguous error in c++?

The declaration of a member with an ambiguous name in a derived class is not an error. The ambiguity is only flagged as an error if you use the ambiguous member name. For example, suppose that two classes named A and B both have a member named x , and a class named C inherits from both A and B .

How do you remove ambiguous reference in C#?

Solution 1 You can: 1) Remove the using statement for the one you are not interested in. 2) Fully qualify the object when you reference it, by prefixing "File" with either "Scripting." or "System.IO."


1 Answers

I think the compiler is right, however weird it may be. Template argument deduction rules are different from substitution. The ambiguity in overloaded function resolution in a template parameter pack context does not necessarily mean failure.

See [temp.deduct.call]/p6:

When P is a function type, function pointer type, or pointer to member function type:

...

-- If the argument is an overload set (not containing function templates), trial argument deduction is attempted using each of the members of the set. If deduction succeeds for only one of the overload set members, that member is used as the argument value for the deduction. If deduction succeeds for more than one member of the overload set the parameter is treated as a non-deduced context.

So for the last argument of the parameter pack we're in a non-deduced context (not an error).

And [temp.arg.explicit]/p3:

... A trailing template parameter pack not otherwise deduced will be deduced to an empty sequence of template arguments. ...

So it seems to me (although this last bit doesn't say it explicitly for partially deduced parameter packs) the ambiguous function pointer is simply thrown away at deduction phase, and subsequently the substitution fails because it is trying to substitute 3 arguments into a deduced 2-argument function. It never gets to a point when it needs to resolve ambiguity.

like image 159
rustyx Avatar answered Oct 10 '22 09:10

rustyx