C++ - specialising member function template via te

2019-07-29 10:55发布

问题:

I wish to create a class that can convert between arrays of floats and doubles polymorphically. That is, the instance concerned (parameterised by <double> or <float>) and the decision to pass a float* or double* is decided at runtime, not statically.

As a proposed answer to another question, but modified according to this answer (because I understand it's not possible to fully specialise a member function template inside a class), a pure virtual base class BaseDest that provides simple overloaded member functions is sub-classed to define DestImpl<T>. I use this base class to maintain a dynamic collection of DestImpl<T> instances, with varying T. This class provides explicit overloads of the assign() member function; one for a double *, and another for a float *. The idea is that at run-time, BaseDest::assign() is called via a polymorphic pointer or reference, and this in turn calls the correct virtual assign() member function in DestImpl<T>.

Now, it is important that then the non-pointer type of the array matches T in DestImpl<T>, that a fast_copy() function is called (perhaps a memcpy), and when the types do not match a slower statically-cast-item-by-item copy is performed. So the assign() member function offloads this to a templated functor. There are two specialisations for this functor - one where the type parameter of the functor matches the type of DestImpl<T> (and therefore invokes a fast copy), and a fall-back one that catches all other cases (and invokes a slow copy).

However, I am unable to get the following code to compile. The comments show where the compiler error and warning appear - I suspect they are related. What I don't understand is why the second specialisation of apply_helper is unable to be instantiated as apply_helper<double>.

class BaseDest {
public:
  virtual ~BaseDest() {}

  virtual void assign(const double * v, size_t cnt) = 0;
  virtual void assign(const float * v, size_t cnt) = 0;
};

template <typename T>
class DestImpl : public BaseDest {
public:

  void assign(const double * v, size_t cnt) {
    assign_helper<T>()(v, cnt);
  }
  void assign(const float * v, size_t cnt) {
    assign_helper<T>()(v, cnt);  // ERROR: no matching function for call to object of type 'assign_helper<double>'

  }   
protected:

  template <typename U>
  struct assign_helper {
    void operator()(const U * v, size_t cnt) {
      for (size_t i = 0; i < cnt; ++i) {
        //slow_copy(v[i]);
      }
    }
  };

  template <typename U>
  struct assign_helper<T> {  // WARNING: Class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used

    void operator()(const T * v, size_t cnt) {
      //fast_copy(v, cnt);
    }
  };
};

void test() {
  DestImpl<double> d;  // error mentioned above appears when this is present
}

EDIT: here's something that does seem to work - moving the assign_helper struct (now a class) out of the DestImpl<T> class definition. I'm not sure this is the right way to do it, but it does seem to work so far:

// slow copy between different types
template <typename T, typename U>
class assign_helper {
public:
  void operator()(const U *v, size_t cnt) {
    // slow copy
  }
};

// fast copy between same types
template <typename T>
class assign_helper<T, T> {
public:
  void operator()(const T * v, size_t cnt) {
    // fast copy
  }
};


class BaseDest {
public:
  virtual ~BaseDest() {}

  virtual void assign(const double * v, size_t cnt) = 0;
  virtual void assign(const float * v, size_t cnt) = 0;
};

template <typename T>
class DestImpl : public BaseDest {
public:

  virtual void assign(const double * v, size_t cnt) {
    assign_helper<T, double>()(v, cnt);
  }
  virtual void assign(const float * v, size_t cnt) {
    assign_helper<T, float>()(v, cnt);
  }
};

回答1:

template <typename U>
  struct assign_helper<T> {  // WARNING: Class template partial specialization contains a template parameter that can not be deduced; this partial specialization will never be used

The above is the cause of your error. The warning explicitly tells you that this definition will never be used. What you want instead of template < typename U > is template <>.