I've been experimenting with the Curiously Recurring Template Pattern for a generic single-argument functor and have two implementations: one using a template template parameter which works and a second where I try to access the derived Functor::type in the interface class. In the latter example, the compiler (gcc 5.4.0) reports
error: no type named 'type' in 'struct Cube< double >'
template<class T, template<class> class Functor>
class FunctorInterface_1 {
private:
const Functor<T> &f_cref;
public:
FunctorInterface_1() : f_cref(static_cast<const Functor<T>&>(*this)) {}
T operator() ( T val ) const { return f_cref(val); }
}; // FunctorInterface_1 (works)
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
using Ftype = typename Functor::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
I then try to compile with T=double in main() of the following two classes:
template<class T>
struct Square : public FunctorInterface_1<T,Square> {
T operator()( T val ) const { return val*val; }
}; // Square
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Can the FunctorInterface_2/Cube example be modified to work, or is it necessary for the interface class to be templated on T as in the first example? Thanks!
EDIT: Using gcc -std=c++14, I can get the second example to compile and run by using auto return and argument types in FunctorInterface_1::operator(), however, as I understand, auto argument types are not part of the C++14 standard.
EDIT 2: Well I feel a bit thick. I just realized that I could template FunctorInterface_1::operator() on a new parameter, however, for the application I have in mind, I would really like my base class to be able to access types defined in the derived class.
When the line
using Ftype = typename Functor::type;
is processed in the base class, the definition of Functor
is not available. Hence, you can't use Functor::type
.
One way to get around this limitation is to define a traits class.
// Declare a traits class.
template <typename T> struct FunctorTraits;
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
// Use the traits class to define Ftype
using Ftype = typename FunctorTraits<Functor>::type;
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
Ftype operator() ( Ftype val ) const { return f_cref(val); }
}; // FunctorInterface_2 (no type in Functor!)
// Forward declare Cube to specialize FunctorTraits
template<class T> struct Cube;
// Specialize FunctorTraits for Cube
template <typename T> struct FunctorTraits<Cube<T>>
{
using type = T;
};
template<class T>
struct Cube : public FunctorInterface_2<Cube<T>> {
using type = T;
T operator() ( T val ) const { return val*val*val; }
}; // Cube
Working code: https://ideone.com/C1L4YW