On Mon, Feb 1, 2016 at 1:26 AM, Jim Porter <
jvp...@g.rit.edu> wrote:
> I originally asked this on the clang mailing list, but didn't get a
> response. Perhaps this would be a better venue.
>
> The following code attempts to hide "func" via enable_if. Is it valid?
>
> template<typename T> struct always_false : std::false_type {};
> template<typename T, std::enable_if_t<always_false<T>::value, int>...>
> void func(T &) {}
I assume you intended
template<typename ...T, std::enable_if_t<always_false<T>::value, int>...>
void func(T &...) {}
... otherwise the answer is that this is ill-formed, as it attempts to
expand a non-pack.
Your example also violates [temp.param]/11:
"A template parameter pack of a function template shall not be
followed by another template parameter unless that template parameter
can be deduced from the parameter-type-list of the function template
or has a default argument (14.8.2)."
Your enable_if_t pack is not deducible and has no default argument.
> Specifically, I'm wondering about the use of std::enable_if being used in
> the context of a non-type template parameter pack (in this case, a pack of
> ints). If valid, it's a neat trick; in fact, it's already in use in
> libstdc++'s implementation of <experimental/optional>. However, clang
> doesn't understand it and SFINAE is never triggered, presumably because it
> expands it to a pack of zero size before evaluating the enable_if.
>
> My reading of [temp.deduct] p7 seems to suggest that clang is in the wrong
> here: "The substitution [from template argument deduction] occurs in *all*
> types and expressions that are used in the function type and in template
> parameter declarations." (Emphasis mine.) However, I'm not enough of a
> language lawyer to be certain. Can anyone confirm whether this is a valid
> construct?
Substitution into a pack expansion makes N copies of the pattern and
substitutes into each of those copies; see [temp.variadic]/3 and /7.
If N == 0, no substitution into the pack is performed. However, that's
not the complete story, as there clearly are cases where some
substitution into a possibly-empty pack is performed, without
expanding the pack:
template<typename T> struct X {
template<T ...V> struct Y {};
};
X<X<int>> x;
This case is ill-formed by [temp.res]/8, as every valid specialization
of X<X<int>>::Y requires an empty parameter pack. It's at best unclear
from the standard which phases of substitution into a function
template should perform pack expansion. It's probably worth filing a
core issue asking for some clarification here.