c++templatesnon-virtual-interface

No type named 'type' in CTRP derived class


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.


Solution

  • 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