Search code examples
c++c++11templatestemplate-specializationtemplate-argument-deduction

Template type derivation


I need to implement a class, say 'MyClass' using templates.

template<class T>
class MyClass
{
public:


          T var1;
          T1 var2;
        };

There are two member variables var1 and var2. If the class template argument, 'T', is fundamental type (eg: float, double or long double), the types of both the variables var1 and var2 should be the same as the template argument. That is T1 = T in the above example.

But if the template argument is std::complex<T>, I would like to have

T var1;
std::complex<T> var2;

How to implement it in C++11?


Solution

  • A possible solution could be define a simple type traits to extract the correct type

    template <typename T>
    struct myTypeTraits
     { using type = T; };
    
    template <typename T>
    struct myTypeTraits<std::complex<T>>
     { using type = T; };
    

    and MyClass become

    template <typename T>
    class MyClass
     {
       using T0 = typename myTypeTraits<T>::type;
    
       T0 var1;
       T  var2;
     };
    

    If you want to be sure that T is a fundamental type (or do you mean arithmetic?), is a little more complicated.

    A possible way is define a type traits to say (true or false) if a type is a std::complex

    template <typename>
    struct isComplex : public std::false_type
     { };
    
    template <typename T>
    struct isComplex<std::complex<T>> : public std::true_type
     { };
    

    Next modify myTypeTraits with a declaration (no generic definition) and two default bool values

    template <typename T, bool = std::is_fundamental<T>::value, 
                          bool = isComplex<T>::value>
    struct myTypeTraits;
    

    Next two specializations, the first one for fundamentals and the second one for complexes types

    template <typename T>
    struct myTypeTraits<T, true, false>
     { using type = T; };
    
    template <typename T>
    struct myTypeTraits<std::complex<T>, false, true>
     { using type = T; };
    

    The MyClass class remain equals but now give an error if you try to instantiate it with (by example) a std::string.

    The following is a full compiling example

    #include <complex>
    #include <type_traits>
    
    template <typename>
    struct isComplex : public std::false_type
     { };
    
    template <typename T>
    struct isComplex<std::complex<T>> : public std::true_type
     { };
    
    template <typename T, bool = std::is_fundamental<T>::value, 
                          bool = isComplex<T>::value>
    struct myTypeTraits;
    
    template <typename T>
    struct myTypeTraits<T, true, false>
     { using type = T; };
    
    template <typename T>
    struct myTypeTraits<std::complex<T>, false, true>
     { using type = T; };
    
    template <typename T>
    class MyClass
     {
       public:  // public to check with the static_assert()
          using T0 = typename myTypeTraits<T>::type;
    
       private:
          T0 var1;
          T  var2;
     };
    
    int main ()
     {
       MyClass<int>                  mi; // compile
       MyClass<std::complex<float>>  mc; // compile
       // MyClass<std::string>          ms; // compilation error
    
       static_assert( std::is_same<int, decltype(mi)::T0>{}, "!" );
       static_assert( std::is_same<float, decltype(mc)::T0>{}, "!" );
     }