简体   繁体   中英

Function templates - compiler choosing function with different parameter types when calling with the same type

I was playing around with function templates and I stumbled across a weird interaction.

template<class T1, class T2>
void foo(T1, T1);

template<class T1, class T2>
void foo(T1, T2);

//main
foo(1,1)

This calls foo(T1, T2) and I don't understand why. How would that work? Are these functions overloads of each other, and why would the compiler choose the one with different parameter types?

this particular interaction was explained in the first part of Henri Menke's post


After some more messing around I found something weirder

#include <iostream>

template<class T1, class T2>
void foo(T1 a, T1 b)
{
    std::cout << "same\n";
}

template<class T1, class T2>
void foo(T1 a, T2 b)
{
    std::cout << "different\n";
}

int main()
{
    foo(1, 1);
    foo<int, int>(1, 1);
}

In this code I get a result

different
different

but after commenting out the 1st call like

int main()
{
    //foo(1, 1);
    foo<int, int>(1, 1);
}

the result is

same

I'm using VS2015 and if I write the same thing in Ideone ( like here ) the result for the 1st one is

different
same

Could somebody be able to explain what is (or isn't) going on?


By the way I came to the conclusion that the call foo<int, int>(1, 1); should be ambiguous. Both function templates have the same signature then and come from the same template. So that is another thing, why don't they collide?

It is easy to see why this fails by simply deleting the second template, ie having a source file like

template<class T1, class T2>
void foo(T1, T1);

int main()
{
  foo(1,1);
}

This fails in Clang with the error

test.cpp:6:3: error: no matching function for call to 'foo'
  foo(1,1);
  ^~~
test.cpp:2:6: note: candidate template ignored: couldn't infer template argument
      'T2'
void foo(T1, T1);
     ^
1 error generated.

The compiler has no way to deduce the second template parameter T2 .


If you delete the superfluous T2 from the first template and use this kind of source file

template<class T1>
void foo(T1, T1);

template<class T1, class T2>
void foo(T1, T2);

int main()
{
  foo(1,1);
}

The compiler will always choose the the first option (if T1 and T2 are the same of course) because it is more specialised.

Another option is to give T2 a default value. Then the first variant can also be instantiated from foo(1,1) .

template<class T1, class T2 = void>
void foo(T1, T1);

Another interesting thing is this:

#include <iostream>

template<class T1, class T2>
void foo(T1, T1)
{ std::cout << "First choice!\n"; }

template<class T1, class T2>
void foo(T1, T2)
{ std::cout << "Second choice!\n"; }

int main()
{
  foo<int,int>(1,1);
}

This will output at runtime:

First choice!

I'm not entirely sure, but I believe this is because in the case of deduction (even though it is not carried out here), for the first variant the compiler would have to deduce only one type instead of two which makes it the more specialised option.

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