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