1

I know how to use template in C++ but can't understand the code below, especially the last line. It's a operator * overloading for half-precision type. Could someone explain this to me? (Here detail is a namespace)

    /// SFINAE helper for generic half-precision functions.
    /// This class template has to be specialized for each valid combination of argument types to provide a corresponding
    /// `type` member equivalent to \a T.
    /// \tparam T type to return
    template<typename T,typename,typename=void,typename=void> struct enable {};
    template<typename T> struct enable<T,half,void,void> { typedef T type; };
    template<typename T> struct enable<T,expr,void,void> { typedef T type; };
    template<typename T> struct enable<T,half,half,void> { typedef T type; };
    template<typename T> struct enable<T,half,expr,void> { typedef T type; };
    template<typename T> struct enable<T,expr,half,void> { typedef T type; };
    template<typename T> struct enable<T,expr,expr,void> { typedef T type; };
    template<typename T> struct enable<T,half,half,half> { typedef T type; };
    template<typename T> struct enable<T,half,half,expr> { typedef T type; };
    template<typename T> struct enable<T,half,expr,half> { typedef T type; };
    template<typename T> struct enable<T,half,expr,expr> { typedef T type; };
    template<typename T> struct enable<T,expr,half,half> { typedef T type; };
    template<typename T> struct enable<T,expr,half,expr> { typedef T type; };
    template<typename T> struct enable<T,expr,expr,half> { typedef T type; };
    template<typename T> struct enable<T,expr,expr,expr> { typedef T type; };

.... some codes ...   

template<typename T> typename detail::enable<half&,T>::type operator*=(T rhs) { return *this *= static_cast<float>(rhs); }

I understand there are different types of struct enable for different template pairs. But what does the struct enable declarations do above? Is it defining a type inside the structure?(so that that type has type T inside the sturct?) and in the template specifier, what does for example mean? I don't know what expr here is. Is it a type or does it have special meaning?

BLUEPIXY
  • 39,699
  • 7
  • 33
  • 70
Chan Kim
  • 5,177
  • 12
  • 57
  • 112
  • it means `enable` has a `type` if it satisfies any of those combinations. this would be used to ensure `operator *=` only allows specific types (looks like `half` and `expr` are the only possibilities) – kmdreko Aug 11 '17 at 01:54
  • @vu1p3n0x why `half` and `expr` the only possibilies? – Chan Kim Aug 11 '17 at 01:58
  • because those are the only ones that fit the combinations above. there's only 2 template parameters supplied so the other 2 for `enable` default to `void`, so you're only left with the first 2; `` and `` – kmdreko Aug 11 '17 at 02:02
  • @vu1p3n0x so, you mean, by the first line, `struct enable {}` exists only when the template is ? and this applies only to `struct enable {}`? and inside `enable` `type` is defined as `T`.. I didn't know that. Thanks! – Chan Kim Aug 11 '17 at 02:32
  • 3
    The comment at the top explain the purpose... Essentially the first line is used except when there is a specialization. And the last line will essentially define the operator for each of the specialization in between. Since the non-specialized template does not define type, it will not be considered for operator *= because of SFINAE. – Phil1970 Aug 11 '17 at 02:41

1 Answers1

1

As far as I understand, for this particular operator*= only first 3 lines play some role:

template<typename T,typename,typename=void,typename=void> struct enable {};
template<typename T> struct enable<T,half,void,void> { typedef T type; };
template<typename T> struct enable<T,expr,void,void> { typedef T type; };

Moreover, you could rewrite it even simpler and get the same result:

template<typename T,typename> struct enable {};
template<typename T> struct enable<T,half> { typedef T type; };
template<typename T> struct enable<T,expr> { typedef T type; };

The meaning of the operator definiton will not change:

template<typename T> typename detail::enable<half&,T>::type operator*=(T rhs) { return *this *= static_cast<float>(rhs); }

at compile time translates to one of those two:

half& operator*=(half rhs) { return *this *= static_cast<float>(rhs); }
half& operator*=(expr rhs) { return *this *= static_cast<float>(rhs); }

Artificially limiting you to use either half or expr as argument type.

The rest of struct enable template specializations are used elsewhere but not applicable for this particular operator.

WindyFields
  • 2,697
  • 1
  • 18
  • 21
  • 1
    And if you try to `*=` with any other type, the expansion of `typename detail::enable::type` will fail, finding no member `type`. – Caleth Aug 15 '17 at 08:36
  • I didn't know the template specialization and now can understand it. Btw, in the last line, the second `typename` is what I can't understand now. I will read the documents. – Chan Kim Aug 17 '17 at 12:28
  • `typename detail::enable::type` basically tells the compiler that `::type` refers to the name of a type defined inside `enable` struct. Otherwise, `enable::type` could mean "address of a static function called `type`" or "static variable called `type`" and that would be ambiguous. You can check [this question](https://stackoverflow.com/questions/7923369/when-is-the-typename-keyword-necessary) for more details. – WindyFields Aug 17 '17 at 12:38