Why is this template parameter not output?

Edit: I made a simple mistake when using SFINAE. A fix that fixes a compiler error that I mention below. However, I am still wondering why the template parameter cannot be inferred in this case.

I wanted to write a C ++ 14 template metaprogram to calculate the largest common divisor (GCD) for std::integer_sequence . After some elevation, I came up with this almost complete example:

 template <typename T, TA, TB, T... Ints> struct GCD<std::integer_sequence<T, A, B, Ints...>> : GCD<typename std::integer_sequence<T, GCD_pair<T, A, B>::value, Ints...>> {}; template <class T, TA, T B> struct GCD<std::integer_sequence<T, A, B>> : GCD_pair<T, A, B> {}; int main() { using seq = std::integer_sequence<int, 65537, 5, 10>; cout << GCD<seq>::value << endl; return 0; } 

I simply delete the first two elements of the integer sequence and find their GCD using the written GCD_pair metafile. Then I apply GCD to the result of GCD_pair and to the rest of the elements.

The "obvious" implementation of GCD_pair does not compile:

 // This does not work: // type 'T' of template argument '0' depends on a template parameter template <typename T, TM, T N> struct GCD_pair : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; template <typename T, T M> struct GCD_pair<T, M, 0> : std::integral_constant<T, M> {}; 

So I tried another possible implementation using SFINAE:

 // This doesn't work either: // template parameters not deducible in partial specialization template <typename T, TM, TN, typename = void> struct GCD_pair : std::integral_constant<T, M> {}; template <typename T, TM, TN, typename std::enable_if<(M % N != 0)>::type> struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Edit: I made a mistake. See the answer below for a fix for using SFINAE. But I'm still interested in my question:

Why is the template parameter typename std::enable_if<(M % N != 0)>::type not output? Is it ever derived in principle, or can parameters such as this be actually put into practice? In other words, can this be considered as monitoring the implementation of the compiler?

For what it was worth, I was able to implement a slightly different version , essentially โ€œhidingโ€ the conditional ( M % N != 0 ) in the bool template parameter. However, I think both of the above are reasonable implementations , because operator% and comparing with 0 with operator!= Are perfectly defined for all C ++ integral types.

+5
source share
1 answer

It should be:

 template <typename T, TM, T N> struct GCD_pair<T, M, N, typename std::enable_if<(M % N != 0)>::type> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Since you are using C ++ 14, you can also simplify it using std::enable_if_t :

 template <typename T, TM, T N> struct GCD_pair<T, M, N, std::enable_if_t<(M % N != 0)>> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

In both cases, if the condition is applied (M % N != 0) , both the primary template and the specialization are valid after the instance is created, but the specialization, and therefore, is more specialized and, therefore, selected. On the other hand, if the condition does not apply, the specialization is silently discarded due to sfinae rules, but the primary template is still valid and selected in this way.

Note that the type in typename std::enable_if<(M % N != 0)>::type is void when the condition is true.
Because of this, the list of template templates will be theoretically:

 template <typename T, TM, TN, void> struct GCD_pair<T, M, N, void>: std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

However, void not allowed as a parameter of the non-type type template.

Finally, from the standard we have:

Partial specialization corresponds to this actual list of template arguments, if partial template arguments can be deduced from the actual list of template arguments

also:

If the arguments of the partial specialization template cannot be deduced due to the structure of its template-parameter-list and template identifier, the program is poorly formed.

In your case, the fourth specialization parameter cannot be displayed for obvious reasons. Even if it was valid (and it is not, because it leads to void , as mentioned), what you get is a type or non-type parameter for which you do not have an actual type or value.
Suppose you have the following specialization:

 template <typename T, TM, TN, int> struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

How could the compiler output the value for the last template parameter?
It cannot and that is more or less your case. Probably, this should soon detect the fact that the parameter list is poorly formed if the condition on std::enable_if valid, in any case both are errors, and you get one of them from the compilation phase.

What will be useful to you is something like this:

 template <typename T, TM, TN, typename = std::enable_if_t<(M % N != 0)>> struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

Or that:

 template <typename T, TM, TN, std::enable_if_t<(M % N != 0)>* = nullptr> struct GCD_pair<T, M, N, void> : std::integral_constant<T, GCD_pair<T, N, M % N>::value> {}; 

In any case, both of them are invalid, since the default arguments are not allowed in partial specialization.

+5
source

Source: https://habr.com/ru/post/1257491/


All Articles