What I need write this second SFINAE constructor?

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?

>Solution :

If I write another SFINAE constructor like this: … 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.

The compiler doesn’t generate the default constructor if you define any custom constructor, even if that constructor is disabled by SFINAE.

And since you need to write the non-=deleted constructor, you might as well remove the =deleted one:

template<typename T, typename... Ts>
struct XYZ
{
    template <typename U = T, std::enable_if_t<!has_duplicate_types<U, Ts...>(), int> = 0>
    XYZ()
    {
        std::cout << "ok\n";
    }
};

Leave a Reply