0

Consider the following code:

class Helper {
public:
  template<typename taResult, typename taParam> static taResult Cast(const taParam par);
};
template<> inline __m256d Helper ::Cast(const __m256i par) {
  return _mm256_castsi256_pd(par);
}
template<> inline __m256i Helper ::Cast(const __m256d par) {
  return _mm256_castpd_si256(par);
}

I want to add to the Helper a function to handle casts where the parameter and the return types are equals. All my attempts to specialize/overload so far have failed with different compilation errors.

Something like the following in the class body:

template<typename T> static T Cast(const T par) {
  return par;
}
Serge Rogatch
  • 13,865
  • 7
  • 86
  • 158
  • 1
    It looks like your code lost some of its template arguments when being copied here; can you verify it’s correct? – Daniel H Aug 08 '17 at 20:09
  • @DanielH: OP used specializations. – Jarod42 Aug 08 '17 at 20:12
  • So, why not use normal overloading here? You provide 3 overloads: the one taking __m256i, the one taking __m256d and the normal template one with whatever your generic algorithm is. – Javier Martín Aug 08 '17 at 20:19
  • Seems like defining casting operators in `_m256d` and `__m256i` solves this problem without the need for the `Helper` class. Is there a reason that approach hasn't been taken? – Jonathan Mee Aug 08 '17 at 20:21
  • @Jarod42 Yeah, I got that, I was just expecting that it needed to say `Helper::Cast<_m256d, _m256i>`. – Daniel H Aug 08 '17 at 20:37
  • @JavierMartín, that seems to give an "ambiguous overload" compilation error. – Serge Rogatch Aug 08 '17 at 20:39
  • @JonathanMee , I need functions to be members of a class to keep the code clean. I provided the functions as members of a class here because I thought that the solution may differ depending on whether they are members of a class or not. – Serge Rogatch Aug 08 '17 at 20:40
  • @SergeRogatch Maybe then both __m256d and __m256i are both typedefs to the same type. If I say `using MyInt = int` that does not mean that I can have two different `void f(int)` and `void f(MyInt)`. Typedefs and alias are still the same type underneath. – Javier Martín Aug 08 '17 at 21:28

4 Answers4

3

You cannot partial specialize function, and your overload would be ambiguous.

You can add class which you can partial specialize though:

template <typename To, typename From> struct CastImpl;

template <typename T> struct CastImpl<T, T>
{
    T operator()(T t) const { return t; }
};

template <> struct CastImpl<__m256d, __m256i>
{
    __m256d operator()(__m256i t) const { return _mm256_castsi256_pd(t); }
};

template <> struct CastImpl<__m256i, __m256d>
{
    __m256i operator()(__m256d t) const { return _mm256_castpd_si256(t); }
};

and then

class Helper {
public:
    template<typename taResult, typename taParam>
    static taResult Cast(const taParam par)
    { 
        return CastImpl<taResult, taParam>{}(par);
    }
};
Jarod42
  • 203,559
  • 14
  • 181
  • 302
1

No you can not, because that would be an attempt to partially specialize a function, which is not allowed. Instead, you'd have to use an intermediate template class, which than can be specialized.

I can provide example if needed.

SergeyA
  • 61,605
  • 5
  • 78
  • 137
1

You can use a helper class/struct template to implement Helper::Cast.

Here's a simple program that has uses a few shortcuts to demonstrate the concept.

using __m256d = double;
using __m256i = int;

template<typename taResult, typename taParam> struct RealHelper;

class Helper
{
   public:
      template<typename taResult, typename taParam> static taResult Cast(const taParam par)
      {
         return RealHelper<taResult, taParam>::doit(par);
      }

   private:

};

template <> struct RealHelper<__m256d, __m256i>
{
   inline static __m256d doit(const __m256i par)
   {
      // return _mm256_castsi256_pd(par);
      return par;
   }
};

template <> struct RealHelper<__m256i, __m256d>
{
   inline static __m256i doit(const __m256d par)
   {
      // return _mm256_castpd_si256(par);
      return par;
   }
};

template <typename T> struct RealHelper<T, T>
{
   inline static T doit(const T par)
   {
      return par;
   }
};

int main()
{
   auto v1 = Helper::Cast<int, double>(10);
   auto v2 = Helper::Cast<double, int>(20);
   auto v3 = Helper::Cast<int, int>(30);
   auto v4 = Helper::Cast<double, double>(40);
}
R Sahu
  • 204,454
  • 14
  • 159
  • 270
0

I want to add to the Helper a function to handle casts where the parameter and the return types are equals.

What about using SFINAE to enable/disable a Cast() version according the value of std::is_same<taResult, taParam>::value ?

A simplified example

#include <iostream>
#include <type_traits>

struct Helper
 {
   template <typename taR, typename taP>
   static std::enable_if_t<false == std::is_same<taR, taP>::value, taR>
      Cast (taP const & par)
    { std::cout << "different Cast" << std::endl; return {}; }

   template <typename taR, typename taP>
   static std::enable_if_t<true == std::is_same<taR, taP>::value, taR>
      Cast (taP const & par)
    { std::cout << "equal Cast" << std::endl; return par; }
 };

template <>
int Helper::Cast<int, long> (long const & par)
 { std::cout << "int/long Cast" << std::endl; return {}; }

template <>
long Helper::Cast<long, int> (int const & par)
 { std::cout << "long/int Cast" << std::endl; return {}; }

int main()
 {
   Helper::template Cast<int>(0);      // print "equal Cast"
   Helper::template Cast<int>(0L);     // print "int/log Cast"
   Helper::template Cast<long>(0);     // print "long/int Cast"
   Helper::template Cast<long>("foo"); // print "different Cast"
 }
max66
  • 65,235
  • 10
  • 71
  • 111
  • It gives a compilation error if I try to specialize `Cast()` for specific types (like `__m256d` and `__m256i` in my code example in the question). – Serge Rogatch Aug 08 '17 at 20:32
  • @SergeRogatch - modified example with a couple of specializations – max66 Aug 08 '17 at 20:38