c++ - Why can't my Curiously Recurring Template Pattern (CRTP) refer to the derived class's typedefs? -
this question has answer here:
when using curiously recurring template pattern, unable refer typedefs belonging derived class only if attempt reference them base class; gcc complains no type named 'mytype' in class derived<...>. seems inconsistent otherwise possible using typedefs, templates, , curiously recurring relationships.
consider:
/* crtp.cpp */ #include <iostream> using namespace std; // case 1. simple. class base { public: typedef int a_t; a_t foo; }; class derived : public base { a_t bar; }; // case 2. template. template<typename t> class tbase { public: typedef t b_t; t foo; }; template <typename t> class tderived : public tbase<t> { typename tbase<t>::b_t bar; }; // case 3. curiously recurring. template <typename t, typename d> class tcuriousbase { public: typedef t c_t; c_t foo; }; template <typename t> class tcuriousderived : public tcuriousbase<t,tcuriousderived<t> > { typename tcuriousbase<t,tcuriousderived<t> >::c_t bar; }; // case 4. curiously recurring member reference. template <typename t, typename d> class tcuriousmemberbase { public: t foo; t get() { return static_cast<d*>(this)->bar; } }; template <typename t> class tcuriousmemberderived : public tcuriousmemberbase<t, tcuriousmemberderived<t> > { public: t bar; tcuriousmemberderived(t val) : bar(val) {} }; // case 5. curiously recurring typedef reference. template <typename t, typename d> class tcurioustypebase { public: typedef t d_t; d_t foo; typename d::c_t baz; }; template <typename t> class tcurioustypederived : public tcurioustypebase<t, tcurioustypederived<t> > { public: typedef t c_t; typename tcurioustypebase<t,tcurioustypederived<t> >::d_t bar; }; // entry point int main(int argc, char **argv) { derived::a_t 1 = 1; tderived<double>::b_t 2 = 2; tcuriousderived<double>::c_t 3 = 3; double 4 = tcuriousmemberderived<double>(4).get(); tcurioustypebase<double, tcuriousderived<double> >::d_t 5 = 5; // tcurioustypederived<double>::d_t 6 = 6; /* fails */ cout << 1 << endl; cout << 2 << endl; cout << 3 << endl; cout << 4 << endl; cout << 5 << endl; // cout << 6 << endl; } from (1), see typedefs indeed inherited base derived; typedef declared in base class can accessed through derived class.
from (2), see still true if both classes templates.
from (3), see typedef inheritance can still exist in presence of curiously recurring template relationship; refer base's typedef via derived class in our declaration of three.
from (4), see member variables of derived class may readily accessed base class.
from (5), see can access typedef defined on template parameter (this works when declare five using types not related inheritance).
as make template parameter derived class in (6), however, typedef becomes inaccessible, though seemingly well-defined member variable in derived class.
why this?
this "circular dependecies", or "incomplete-type" alter-ego.
template "meta-programming" "programming types", requires level of sematics known instantiate types.
consider analogy:
class a; //forwarded class b { a* pa; //good: know how wide pointer is, no matter if don't know yet a. a; // bad: don't know how space requires }; class { float m; }; // know, it's late this can solved placing before b, if is
class { b m; }; thhere no other solution pointers, since recursion infinite. (a should contain itself, not refer copy)
now, with same analogy, let's program "types":
template<class d> class { typedef typename d::inner_type my_type; //required d known when a<d> instantiated... my_type m; // ... otherwise cannot know how wide a<d> be. }; this declaration not bad,until start define d ...
class d: //here know d exist public a<d> //but size depende on d definition... { .... typedef long double; inner_type .... }; // ....we know starting here so, basically, don't know (yet) how wide @ time need use create d.
one way break "circularity" use "traits classes" outside of crt loop:
struct traits { typedef long double inner_type; .... }; template<class d, class traits> class { // easy: traits not depend on typedef typename traits::inner_type my_type; .... }; template<class traits> class d: public a<d, traits> { typedef typename traits::inner_type inner_type; }; an can declare
typedef d<traits> d_inst; in other words, coherence between a::my_type , d::inner_type ensured traits::inner_type, definition independent.
Comments
Post a Comment