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