I'm trying to figure out how implicit instantiation of function arguments works. For some reason in the first example, the bar()
call interprets the {12,41}
as an initializer list. If I change the foo
signature to auto foo(std::pair<int,int> bar)
the {12,41}
is implicitly converted to an std::pair<int,int>
.
For example
template<typename T, typename U>
auto foo(std::pair<T,U> arg) { return arg; }
auto bar() { return foo({12,41}); }
fails with error:
<source>: In function 'auto bar()':
104 : <source>:104:50: error: no matching function for call to 'foo(<brace-enclosed initializer list>)'
auto bar() { return foo({12,41}); }
^
103 : <source>:103:6: note: candidate: 'template<class T, class U> auto foo(std::pair<_T1, _T2>)'
auto foo(std::pair<T,U> arg) { return arg; }
^~~
103 : <source>:103:6: note: template argument deduction/substitution failed:
104 : <source>:104:50: note: couldn't deduce template parameter 'T'
auto bar() { return foo({12,41}); }
^
but
auto foo(std::pair<int,int> arg) { return arg; }
auto bar() { return foo({12,41}); }
works. Would someone care to elaborate why?
For the usual reason: implicit conversions tend to not happen in the same way (or at all) with templated functions. In your second example:
auto foo(std::pair<int,int> arg) { return arg; }
auto works() { return foo({12,41}); }
foo
is a function, not a function template. std::pair
has a non-explicit, two argument constructor: http://en.cppreference.com/w/cpp/utility/pair/pair. A brace pack with n entries can be implicitly converted to a type that has an n-ary implicit constructor. So you get implicit conversion from {12,31}
to std::pair<int, int>
.
When a function template is called, template deduction will try to deduce the templates so that the arguments match. In this process, the full range of implicit conversions is not available. Only a very special few conversions are allowed: http://en.cppreference.com/w/cpp/language/template_argument_deduction. Basically, CV conversions, derived-to-base conversions, and some pointer qualification and function pointer conversions.
So:
template <class T, class U>
auto foo(std::pair<T, U> arg) { return arg; }
auto bar() { return foo({12,41}); }
Fails, because when foo
is called, it tries to substitute template parameters into foo
to make the call arguments and the parameters match up, but they cannot be made to match up precisely enough.
I am not commenting on the pair<auto, auto>
syntax, but the situation is exactly the same with the more explicit syntax so I don't think that's really the heart of the issue.
This has been discussed before on SO here: C++ implicit type conversion with template, so it's worth reading for more information. Although, note that the accepted answer there incorrectly says that only CV qualification changes are allowed when matching a function template; that isn't quite true (maybe I'll propose an edit there).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With