zennehoy zennehoy - 4 months ago 9
C++ Question

How does using ellipses for SFINAE work?

When using SFINAE to select constructor overloads in the past, I have typically used the following:

template <typename T>
class Class {
public:
template <typename U = T, typename std::enable_if<std::is_void<U>::value, int>::type=0>
Class() {
std::cout << "void" << std::endl;
}

template <typename U = T, typename std::enable_if<!std::is_void<U>::value, int>::type=0>
Class() {
std::cout << "not void" << std::endl;
}
};


However, I just came across this alternative:

template <typename U = T, typename std::enable_if<std::is_void<U>::value>::type...>
Class() {
std::cout << "void" << std::endl;
}


Considering that the following is illegal ...

template <typename U = T, void...> // ERROR!
Class() { }


... how does the alternative above using ellipses rather than a non-type template argument work?




Full code: http://coliru.stacked-crooked.com/a/64a1aaf13ce6099b

Answer

My previous answer was wrong. Sorry. I'm just going to fix it.


This declaration:

template <typename U = T, void...>
Class() { }

violates [temp.res]/8:

The program is ill-formed, no diagnostic required, if [...] every valid specialization of a variadic template requires an empty template parameter pack

It's no diagnostic required, but the compiler chooses to issue one anyway. Either way, the code is ill-formed.

On the other hand

template <typename U = T, std::enable_if_t<std::is_void<U>::value>...>
Class() { }

doesn't violate this requirement. We have an empty pack, so we don't run afoul of the fact that you can't use void as a non-type template parameter. Additionally, a hypothetical specialization of enable_if could provide a type that isn't void there so it's not ill-formed due to the above limitation.