Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Conditional compilation and template

Tags:

c++

templates

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 have no m_result at all.
But, the compiler does not allow instantiate a void type.
One of decision is to create a specialization.

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

But I don't what to support the almost identical code.
How can I don't instantiate m_result?
I can use C++17. Thanks!

like image 216
Alexey Subbota Avatar asked Jan 17 '18 09:01

Alexey Subbota


Video Answer


2 Answers

You could place 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 a the specialization of the class C is cleaner since all member of a template class should depend on all the template parameter (otherwise you should split your class in order to avoid code bloat).

So I would prefer to fully specialize C, and make part of the class C that are independent of T, a base class of 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 could also specialize member funtion by member function, but I find it less clean.

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

like image 85
Oliv Avatar answered Oct 22 '22 20:10

Oliv


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 stored m_result into policy struct only for non-void types due to partial template specialization.

like image 24
Daniel Langr Avatar answered Oct 22 '22 20:10

Daniel Langr