简体   繁体   中英

Why does the compiler not give an ambiguous reference error?

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.

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.

Justin is correct. Running GCC through a debugger leads to these lines of code:

cp_parser_lookup_name(cp_parser*, tree_node*, tag_types, bool, bool, bool, tree_node**, unsigned int) () at ../../gcc/cp/parser.c:24665
24665   {
(gdb) 
24667     tree object_type = parser->context->object_type;
(gdb) 
24670     if (ambiguous_decls)
(gdb) 
24665   {
(gdb) 
24667     tree object_type = parser->context->object_type;
(gdb) 
24670     if (ambiguous_decls)
(gdb) 
24676     parser->context->object_type = NULL_TREE;

...

(gdb) list 24670
24665   {
24666     tree decl;
24667     tree object_type = parser->context->object_type;
24668   
24669     /* Assume that the lookup will be unambiguous.  */
24670     if (ambiguous_decls)
24671       *ambiguous_decls = NULL_TREE;
24672   
24673     /* Now that we have looked up the name, the OBJECT_TYPE (if any) is
24674        no longer valid.  Note that if we are parsing tentatively, and

And this is the actual code that emits the diagnostic:

6914                        complain);
(gdb) 

test.cpp:9:24: error: too many arguments to function ‘void bar(Args&& ...) [with Args = {}]’
     bar(1, 2.0, &change);
                        ^
test.cpp:2:6: note: declared here
 void bar(Args&&...) {}

...

(gdb) list 6914
6909              /* All other function calls.  */
6910              postfix_expression
6911            = finish_call_expr (postfix_expression, &args,
6912                        /*disallow_virtual=*/false,
6913                        koenig_p,
6914                        complain);
6915    
6916            if (close_paren_loc != UNKNOWN_LOCATION)
6917              {
6918            location_t combined_loc = make_location (token->location,

Skipping through a bunch of stuff (as it would make this answer unnecessarily long), the actual error occurs during overload resolution:

(gdb) 
add_candidates (fns=0x7fffeffb0940, first_arg=first_arg@entry=0x0, args=args@entry=0x7fffeff9baf0, return_type=return_type@entry=0x0, explicit_targs=0x0, 
    template_only=false, conversion_path=0x0, access_path=0x0, flags=1, candidates=0x7fffffffd320, complain=3) at ../../gcc/cp/call.c:5302
5302      for (; fns; fns = OVL_NEXT (fns))
(gdb) 
5365    }
(gdb) 
perform_overload_resolution (complain=3, any_viable_p=<synthetic pointer>, candidates=0x7fffffffd320, args=0x7fffeff9baf0, fn=<optimized out>)
    at ../../gcc/cp/call.c:4036
4036      *candidates = splice_viable (*candidates, false, any_viable_p);
(gdb) 
build_new_function_call(tree_node*, vec<tree_node*, va_gc, vl_embed>**, bool, int) () at ../../gcc/cp/call.c:4111
4111                          complain);
(gdb) 
4115          if (complain & tf_error)
(gdb) 
4119          if (!any_viable_p && candidates && ! candidates->next
(gdb) 
4120              && (TREE_CODE (candidates->fn) == FUNCTION_DECL))
(gdb) 
4121            return cp_build_function_call_vec (candidates->fn, args, complain);

The error occurs in convert_arguments :

(gdb) list 3611
3606          allocated = make_tree_vector ();
3607          params = &allocated;
3608        }
3609    
3610        nargs = convert_arguments (parm_types, params, fndecl, LOOKUP_NORMAL,
3611                       complain);
3612      if (nargs < 0)
3613        return error_mark_node;
3614    
3615      argarray = (*params)->address ();

Finally, the diagnostic is emitted in error_num_args because if (TREE_CODE (TREE_TYPE (fndecl)) == METHOD_TYPE) is false.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM