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);
}
};