Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What makes the overload fail between these two function templates?

Below is the pretty short example.

#include <utility>

template<typename T, typename = void>
struct A {};

template<typename T, typename U>
void f(A<std::pair<T,U>>) {}

template<typename U>
void f(A<std::pair<int,U>, std::enable_if_t<std::is_same_v<int,U>>>) {}

int main() {
  A<std::pair<int, int>> x;
  f(x);
}

The error is clear enough

uffa.cpp: In function ‘int main()’:                                                                                                                                                                                                                           
uffa.cpp:22:4: error: call of overloaded ‘f(A<std::pair<int, int> >&)’ is ambiguous                                                                                                                                                                           
   22 |   f(x);                                                                                                                                                                                                                                                
      |   ~^~~                                                                                                                                                                                                                                                 
uffa.cpp:10:6: note: candidate: ‘void f(A<std::pair<_T1, _T2> >) [with T = int; U = int]’                                                                                                                                                                     
   10 | void f(A<std::pair<T,U>>) {}                                                                                                                                                                                                                           
      |      ^                                                                                                                                                                                                                                                 
uffa.cpp:18:6: note: candidate: ‘void f(A<std::pair<int, U>, typename std::enable_if<is_same_v<int, U>, void>::type>) [with U = int; typename std::enable_if<is_same_v<int, U>, void>::type = void]’                                                          
   18 | void f(A<std::pair<int,U>, std::enable_if_t<std::is_same_v<int,U>>>) {}                                                                                                                                                                                
      |      ^

But I don't understand why having int as a fixed template argument in the second overload doesn't make it more specialized. After all, if I remove , std::enable_if_t<std::is_same_v<int,U>> from it, then it is preferred.

like image 244
Enlico Avatar asked Nov 06 '22 01:11

Enlico


1 Answers

Even though this is language-lawyer, I'm going to provide a layman explanation.

Yes, the second overload fixes the first parameter of pair as int, while the first one doesn't.

But, on the other hand, the first overload fixes the second parameter of A as void, while the second one doesn't.

Your functions are equivalent to those:

template <typename T, typename U>
void f(A<std::pair<T, U>, void>) {}

template <typename U>
void f(A<std::pair<int,U>, blah-blah<U>>) {}

So none of them is more specialized than the other.


The code will work if you use more a conventional SFINAE:

template<typename U, std::enable_if_t<std::is_same_v<U, int>, std::nullptr_t> = nullptr>
void f(A<std::pair<int,U>>) {}

Or C++20 concepts:

template <std::same_as<int> U>
void f(A<std::pair<int,U>>) {}
like image 51
HolyBlackCat Avatar answered Nov 16 '22 05:11

HolyBlackCat