How to use SFINAE to create two different implementations of the same method

I read some articles about SFINAE, but I can not find a solution for my case. Here is what I want to do:

#include <type_traits> struct CByteArray {}; struct HLVariant { HLVariant() {} HLVariant(const HLVariant&) {} HLVariant(const CByteArray&) {} }; template <typename T> struct Serializer { static inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& value) { static_assert(std::is_pod<T>::value, "Not a POD type"); return CByteArray(); } static inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T& value) { return Serializer<HLVariant>::serialize(HLVariant(value)); } }; template <> struct Serializer<HLVariant> { static inline CByteArray serialize(const HLVariant& value) { return CByteArray(); } }; int main() { int i = 0; Serializer<int>::serialize(i); Serializer<CByteArray>::serialize(CByteArray()); Serializer<HLVariant>::serialize(HLVariant()); return 0; } 

But of course, I get error C2039: 'type' : is not a member of 'std::enable_if<false,CByteArray>'

How to achieve what I want?

In addition, it would be possible to somehow reorganize the Serializer so that the template parameter could be implicitly displayed - Serializer::serialize(i); instead of Serializer<int>::serialize(i); ?

+8
source share
2 answers

To use std::enable_if<condition> , you must be in the template on top of the condition. One option is to declare your function as a template with a default argument

 template <typename T> struct Serializer { template<bool pod = std::is_pod<T>::value> // template over condition static typename std::enable_if<pod, CByteArray>::type serialize(const T& value) { return CByteArray(); } template<bool pod = std::is_pod<T>::value> static typename std::enable_if<!pod, CByteArray>::type serialize(const T& value) { return Serializer<HLVariant>::serialize(HLVariant(value)); } }; template<> struct Serializer<HLVariant> { static CByteArray serialize(const HLVariant&); }; 

Alternatively, you can apply SFINAE directly to the scope of the class template:

 template<typename T, typename = void> struct Serializer; template<> struct Serializer<HLVariant> { static CByteArray serialize(const HLVariant&) { return CByteArray(); } }; template<typename T> struct Serializer<T,typename std::enable_if<is_pod<T>::type> { static CByteArray serialize(const T&) { return CByteArray(); } }; template<typename T> struct Serializer<T,typename std::enable_if<!is_pod<T>::type> { static CByteArray serialize(const T&value) { return Serializer<HLVariant>::serialize(HLVariant(value)); }; 

Or you can get rid of the Serializer class and declare it directly through the template functions:

 inline CByteArray serialize(const HLVariant&) { return CByteArray(); } template<typename T> inline typename enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T&) { return CByteArray(); } template<typename T> inline typename enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T&value) { return serialize(HLVariant(value)); } 

By the way, C ++ 14 defines a very useful alias

 template<bool C, typename T> using enable_if_t = typename enable_if<C,T>::type; 

but of course you can do it. This avoids the tedious typename and ::type all the time.

+3
source

SFINAE stands for "Replacement Error - This Is Not An Error." By definition, this means that it applies only when replacing template arguments with parameters in the template definition. Your serialize functions are member functions of a class template; they themselves are not functional templates. The direct answer will be the conversion of functions to function templates ( Live code ):

 template <typename> struct Serializer; template <> struct Serializer<HLVariant> { static CByteArray serialize(const HLVariant& /* value */) { return CByteArray(); } }; template <typename T> struct Serializer { template <typename U = T> static typename std::enable_if<std::is_pod<U>::value, CByteArray>::type serialize(const U& /* value*/) { static_assert(std::is_pod<U>::value, "Not a POD type"); return CByteArray(); } template <typename U = T> static typename std::enable_if<!std::is_pod<U>::value, CByteArray>::type serialize(const U& value) { return Serializer<HLVariant>::serialize(HLVariant(value)); } }; 

I removed the redundant inline , since all functions defined in the class of the class are implicitly built-in, and I moved the Serializer<HLVariant> specialization to make sure that it is correctly declared before the link. It's a little silly to have a class with only static member functions; you can more intelligently implement this as a set of overloaded functions ( Live code ):

 inline CByteArray serialize(const HLVariant& /* value */) { return CByteArray(); } template <typename T> inline typename std::enable_if<std::is_pod<T>::value, CByteArray>::type serialize(const T& /* value*/) { static_assert(std::is_pod<T>::value, "Not a POD type"); return CByteArray(); } template <typename T> inline typename std::enable_if<!std::is_pod<T>::value, CByteArray>::type serialize(const T& value) { return serialize(HLVariant(value)); } int main() { int i = 0; serialize(i); serialize(CByteArray()); serialize(HLVariant()); } 

Given that using SFINAE makes code difficult to read, I would prefer to use the tag dispatch manager in this instance. Instead of controlling the overload resolution of two functions using SFINAE, there is a third function that calls the appropriate implementation for POD or non-POD ( Even more live code ):

 inline CByteArray serialize(const HLVariant& /* value */) { return CByteArray(); } template <typename T> inline CByteArray serialize(std::true_type, const T& /* value*/) { static_assert(std::is_pod<T>::value, "Not a POD type"); return CByteArray(); } template <typename T> inline CByteArray serialize(std::false_type, const T& value) { return serialize(HLVariant(value)); } template <typename T> inline CByteArray serialize(const T& value) { return serialize(std::is_pod<T>{}, value); } 

SFINAE is powerful but dangerous enough to be safe to block for problems that you can solve with simpler tools.

+3
source

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


All Articles