1

I have class for complex numbers:

template<typename T>
struct Complex{
    T r;
    T i;
};

I decided to add function similar to std::get:

template<int X, typename T>
T get(const Complex<T> &a){
   switch(X){
       case 0: return a.r;
       case 1: return a.i;
   }
}

This works OK. Also I know the compiler can optimize it away.

Then I decided to rewrite it in different way:

template<int X,typename T>
T get(const Complex<T> &a);

template<typename T>
constexpr T get<0, T>(const Complex<T> &a){
    return a.r;
}

template<typename T>
constexpr T get<1, T>(const Complex<T> &a){
    return a.i;
}

However this does not compile and I am curious how correct implementation will be?

I tried to check how std::get is made, but I was unable to find anything that was "readable".

leiyc
  • 903
  • 11
  • 23
Nick
  • 9,962
  • 4
  • 42
  • 80

4 Answers4

3

In C++11 you can implement this exercise like:

#include <type_traits>

template<typename T>
struct Complex{
    T r;
    T i;
};


template<int X, typename T>
constexpr typename std::enable_if<X == 0,T>::type
get(const Complex<T> &a){
    return a.r;
}

template<int X, typename T>
constexpr typename std::enable_if<X == 1,T>::type
get(const Complex<T> &a){
    return a.i;
}

Live demo

Partial template specialization is applicable for class templates, not function templates.

In C++14 you can write trivially more concise code using std::enable_if_t.

And in C++17 you may use if constexpr to write a single function template instead of SFINAE overloads.

Mike Kinghan
  • 55,740
  • 12
  • 153
  • 182
1

Function templates cannot be partially specialised.

Another method is tag dispatching that achieves a similar effect using function overloading:

template<int X>
using Int = std::integral_constant<int, X>;

template<typename T> inline T get(const Complex<T> &a, Int<0>) { return a.r; }
template<typename T> inline T get(const Complex<T> &a, Int<1>) { return a.i; }

template<int X, typename T>
inline T get(const Complex<T> &a) { return get(a, Int<X>{}); }
Maxim Egorushkin
  • 131,725
  • 17
  • 180
  • 271
0

I was able to come up with this, but it looks very complex for such an easy task:

namespace complex_impl__{
    template<int X, typename T>
    struct GetHelper{
        static T get(const Complex<T> &a);
    };

    template<typename T>
    struct GetHelper<0, T>{
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<typename T>
    struct GetHelper<1, T>{
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I, T>::get(a);
}

Then I was able to move T into get() method, so I can make less code:

namespace complex_impl__{
    template<int I>
    struct GetHelper{
        template<typename T>
        static T get(const Complex<T> &a);
    };

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}

One may "strip" entire template<int I> struct GetHelper class:

namespace complex_impl__{
    template<int I>
    struct GetHelper;

    template<>
    struct GetHelper<0>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.r;
        }
    };

    template<>
    struct GetHelper<1>{
        template<typename T>
        constexpr static T get(const Complex<T> &a){
            return a.i;
        }
    };
}

template<int I,typename T>
constexpr T get(const Complex<T> &a){
    return complex_impl__::GetHelper<I>::get(a);
}
Nick
  • 9,962
  • 4
  • 42
  • 80
0

The problem is that you can't partially specialise function templates. Please read C++ template specialisation of function and Getting “illegal use of explicit template arguments” when doing a pointer partial specialization for a class method, which Nick describes the same solution of.

To get the template specialisation to work, please see Nick's answer.

Your solution does seem fine though, I would just change the switch into an if constexpr.

#include <iostream>

template<typename T>
struct Complex 
{
    T r;
    T i;
};

template<int X, typename T>
constexpr T get(const Complex<T> &a)
{
    if constexpr(X == 0)
    {
        return a.r;
    }
    else if constexpr (X == 1)
    {
        return a.i;
    }
}

int main()
{
    Complex<int> test;
    test.r = 1;
    test.i = 12;

    std::cout << get<0>(test) << std::endl;
    std::cout << get<1>(test) << std::endl;

    std::cin.get();

}