The following class has four overloads of function f
. When T!=int
, all overloads have unique parameter lists, but when T=int
, all overloads have the same parameter lists. To make it compile even when int
and T
are identical, I tried to disable three of the functions using a concept. Despite that, the compiler generates an error because the three disabled overloads have identical signatures (https://godbolt.org/z/8aeWdrTs6):
#include <concepts>
template <class T>
struct S {
void f(T, T) { /*...*/ }
void f(T, int) requires (!std::same_as<int, T>) { /*...*/ }
void f(int, T) requires (!std::same_as<int, T>) { /*...*/ }
void f(int, int) requires (!std::same_as<int, T>) { /*...*/ }
};
int main() {
S<int> s;
}
clang says error: multiple overloads of 'f' instantiate to the same signature 'void (int, int)'
, and gcc says something similar.
I can work around this by introducing artificial syntactic differences in the constraints, e.g. (https://godbolt.org/z/TajvPYPo4):
void f(int, T) requires (!std::same_as<int, T> && true) { /*...*/ }
void f(int, int) requires (!std::same_as<int, T> && true && true) { /*...*/ }
Unelegant and crude, but we could also use SFINAE tricks to disable certain overloads:
#include <concepts>
#include <string>
template <class T>
struct S {
void f(T, T) { /*...*/ }
template<typename E = T>
auto f(E, int) -> std::enable_if_t<std::is_convertible_v<E, T> && !std::is_same_v<int, T>, void> { /*...*/ }
template<typename E = T>
auto f(int, E) -> std::enable_if_t<std::is_convertible_v<E, T> && !std::is_same_v<int, T>, void> { /*...*/ }
template<typename E = T>
auto f(int, int) -> std::enable_if_t<std::is_convertible_v<E, T> && !std::is_same_v<int, T>, void> { /*...*/ }
};
int main() {
S<int> s;
s.f(0, 0);
S<std::string> sd;
sd.f("hello", "world");
sd.f("hello", 0);
sd.f(0, "world");
sd.f(0, 0);
}
This works because the overloads using E are templates and are not instantiated until overload.