I wanted to activate a class with parameter packs when all types in the parameter pack is distinct. I wrote a small helper function like this that works with no problem:
template<typename T, typename... Ts>
constexpr bool has_duplicate_types() {
if constexpr (sizeof...(Ts) == 0)
return false;
else
return ((std::is_same_v<std::decay_t<T>, std::decay_t<Ts>> || ...) || has_duplicate_types<Ts...>());
}
When I tried to activate my class using SFINAE, I tried several method and the working one was this:
template<typename T, typename dummy = void, typename... Ts>
struct XYZ_imp {
XYZ_imp() {
std::cout << "ok\n";
}
};
template<typename T, typename... Ts>
struct XYZ_imp<T, std::enable_if_t<has_duplicate_types<T, Ts...>()>, Ts...> {
XYZ_imp() = delete;
};
template<typename T, typename... Ts>
using XYZ = XYZ_imp<T, void, Ts...>;
But as you see, it is too much code. But when I tried to write it like this:
template<typename T, typename... Ts>
struct XYZ {
template<typename U = T, std::enable_if_t<has_duplicate_types<U, Ts...>(), int> = 0>
XYZ() = delete;
};
I don't know why it is not working. It works as expected when there is a similar type like XYZ<int,int>
and it says constructor is deleted, but when types differ like XYZ<int,double>
, I get following error:
no matching function for call to 'XYZ<int, double>::XYZ()'
If I write another SFINAE constructor like this:
template<typename T, typename... Ts>
struct XYZ {
template<typename U = T, std::enable_if_t<has_duplicate_types<U, Ts...>(), int> = 0>
XYZ() = delete;
template<typename U = T, std::enable_if_t<!has_duplicate_types<U, Ts...>(), int> = 0>
XYZ() {
std::cout << "ok\n";
}
};
It will work, but I have no idea why I need to write 2nd one and it does not pick defaulted one without it automatically. Any idea why?
requires
. If no,static_assert
is the easiest option (but it makes your class SFINAE-unfriendly). – Weatherford