Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Implicit instantiation of function argument

Tags:

c++

c++17

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?

like image 875
aerkenemesis Avatar asked Aug 22 '17 13:08

aerkenemesis


1 Answers

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).

like image 151
Nir Friedman Avatar answered Oct 19 '22 16:10

Nir Friedman