Conditional Compilation and Pattern

Suppose I have a code:

template <typename T> class C { public: T f() { return m_result; } void todo() { m_result = doit<T>(); } private: T m_result; }; 

If T is void , I want to return void and not have m_result at all.
But the compiler does not allow you to create an instance of type void .
One solution is to create a specialization.

 template <> class C<void> { /* ... */ } 

But I do not support almost identical code.
How can I not create an instance of m_result ?
I can use C ++ 17. Thanks!

+5
source share
3 answers

You can put the data in a base class, then use if constexpr :

 template<class T> struct C_data{ T m_result; }; template<> struct C_data<void>{ }; template<class T> class C: C_data<T> { static constexpr auto is_void = std::is_same_v<T,void>; public: auto f(){ if constexpr(is_void) return this->m_result; else return; } void todo(){ if constexpr(is_void) this->m_result = doit<T>(); else doit<T>(); } }; 

But it can be argued that the specialization of class C is cleaner, since the entire member of the template class must depend on all the parameters of the template (otherwise you must separate your class to avoid bloating the code).

Therefore, I would prefer to completely specialize C and make part of class C, which are independent of T, the base class C:

 class C_base{ //any thing that is independent of T; }; template<class T> class C: public C_base{ //any thing that depend on T }; template<> class C<void>: public C_base{ //any thing that depend on T; }; 

You can also specialize a member function with a member function, but I believe it is less pure.

You will find this latest code structure in almost all the headers of standard library implementations.

+4
source

This works for me:

 #include <type_traits> template <typename T> T doit() { return T{}; } template <typename T> struct result_policy { T m_result; }; template <> struct result_policy<void> { }; template <typename T> class C : private result_policy<T> { public: T f(){ if constexpr (!std::is_void_v<T>) return result_policy<T>::m_result; } void todo() { if constexpr(!std::is_void_v<T>) result_policy<T>::m_result = doit<T>(); } }; int main() { C<int> ci; ci.todo(); int i = ci.f(); C<void> cv; cv.todo(); cv.f(); } 

I used if constexpr from C ++ 17 to work with m_result and saved m_result in a struct struct only for non-empty types due to the partial specialization of templates.

+2
source

If you can use C ++ 17, try with if constexpr , std::is_same_v<> and std::conditional_t<> :

 #include <type_traits> // auxiliary variable template for checking against void type in a more readable way template<typename T> constexpr bool is_void_v = std::is_same_v<T, void>; // auxiliary alias template for determining the type of the data member // in order to prevent the compiler from creating member of type "void" // so if T = void --> data member type as "int" template<typename T> using member_type_t = std::conditional_t<!is_void_v<T>, T, int>; template <typename T> class C{ public: T f(){ return (T)m_result; } // no problem if T = void void todo() { if constexpr(!is_void_v<T>) m_result = doit<T>(); else doit<T>(); } private: member_type_t<T> m_result; }; 

Actually, with C ++ 17, the std::is_void_v<> template already exists with type_traits .

0
source

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


All Articles