No type named 'type' in CTRP derived class
Note: This question has already been answered by @r-sahu, but I'd like to elaborate on this and address clang's output specifically.
The problem can be demonstrated on a much smaller code sample: (@vtt suggested something similar)
template <typename _CRTP>
struct A {
using _C = typename _CRTP::C;
};
struct B : public A<B> {
using C = int;
};
Compiling that with clang will result in completely misleading error message: (godbolt)
<source>:3:32: error: no type named 'C' in 'B'
using _C = typename _CRTP::C;
~~~~~~~~~~~~~~~~^
<source>:6:19: note: in instantiation of template class 'A<B>' requested here
struct B : public A<B> {
^
1 error generated.
GCC's error message is a little more helpful: (godbolt)
<source>: In instantiation of 'struct A<B>':
<source>:6:19: required from here
<source>:3:33: error: invalid use of incomplete type 'struct B'
3 | using _C = typename _CRTP::C;
| ^
<source>:6:8: note: forward declaration of 'struct B'
6 | struct B : public A<B> {
| ^
As suggested in the accepted answer, implementing a trait type fixes the issue:
// this declaration must appear before the definition of A
template <typename _A>
struct a_traits;
template <typename _CRTP>
struct A {
// `a_traits<_CRTP>::type` is an incomplete type at this point,
// but that doesn't matter since `A` is also incomplete
using _C = typename a_traits<_CRTP>::type;
};
// this specialization must appear before the definition of B
template <>
struct a_traits<struct B> { // adding the type specifier `struct` will declare B
using type = int;
};
// specifying the template parameter will complete the type `A<B>`, which works since
// `a_traits<B>` is already complete at this point
struct B : public A<B> {
using C = int;
};
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
You have to understand that when you instantiate Cube<T>
FunctionInterface_2<Cube<T>>
gets instantiated first. This means that Cube<T>
is an incomplete type while this is happening.
So when the compiler gets to the line using Ftype = typename Functor::type;
Functor
is incomplete and you cannot access any of its nested types.
In your case you can change FunctionInterface_2
to:
template<class Functor>
class FunctorInterface_2 {
private:
const Functor &f_cref;
public:
FunctorInterface_2() : f_cref(static_cast<const Functor&>(*this)) {}
template <class TT>
auto operator() ( TT && val ) -> decltype(f_cref(val)) const { return f_cref(val); }
};
So now accessing information about Functor
is delayed until you call the operator()
from FunctionInterface_2
at which point FunctionInterface_2
and Cube
are fully instantiated.
Your code could be simplified to
template<typename TDerived> class
Base
{
using Ftype = typename TDerived::type;
};
template<typename T> class
Derived: public Base<Derived<T>>
{
using type = T;
};
Derived<int> wat;
It does not work because at the point of Base
instantiation Derived
class is not complete, and compiler is not aware of Derived::type
existence yet.