Crtp and type of visibility

I have this puzzle that I am trying to solve, and basically it comes down to the following example:

template <typename CT> struct A { typedef typename CT::VALUE_T FOO; // FOO is dependent on CT }; template <typename CT> struct B { typedef typename CT::BAR BAR; BAR foo() {} }; template <typename DT> struct C : B<C<DT> > { typedef DT VALUE_T; typedef typename A<C>::FOO BAR; }; int main () { C<int> c; } 

I can try to explain above (I tried and deleted the text three times!), But basically the requirements:

  • C must inherit from B dialed using C (using CRTP), i.e. B<C<>>
  • C is the only one that can instantiate A (i.e., A must be typed using C )
  • A is the only one that can determine FOO ( FOO depends on the type of CT , this relationship is more complicated than the one presented)

The problem (as you can see with the above code) is that the BAR type is only available inside C , and this is incomplete when B is created, so B does not see the BAR type of the argument of the CT template argument ( C<int> ). Unfortunately, in B the BAR type is used as arguments for functions and return types (i.e., it is not limited to the scope - as a result, I cannot just move the input area to the area).

Is there any way around this? I can’t break the above relationship (if not as a last resort). Presumably with C ++ 11, I could use auto and get around the need to have a BAR typedef in B , however this is not an option at the moment.

EDIT: following @bitmasks comment, some more info.

  • The code in A and B used in several binary files in different situations, the only unique situation in this case is that C comes from B , in other cases C owns an instance of something derived from B
  • Template arguments can be changed (in A and B ) if they can be set by default for values ​​that do not require changing existing applications A and B The same set of types must be available either as a template parameter, or by default, or in some other mechanism.

I use templates here simply because I need a tight connection, and I needed the flexibility to use the code in different situations.

Description of components:

  • A best described as a container, and FOO indeed an iterator, what it contains is determined using the typedef parameter of the template
  • B best described as a base class that contains a set of functions that are called by some components belonging to an instance of C In previous cases, these components were given a reference to things derived from B (and these things also belong to C ), in which case I provide a reference to C myself.

The main complication arises when accessing container A , previously the relationship between B and C is that C has an instance of B , but now C is an instance of B - this change in semantics breaks the way types are introduced into classes.

+6
source share
1 answer

I think you can deal with the circular typedef requirement with the default template options. The following works as intended (as far as I understand your question) and leaves you (almost) all the freedom of your source code:

 template <typename CT, typename CTVALUE_T = typename CT::VALUE_T> struct A { //typedef typename CT::VALUE_T FOO; // FOO is dependent on CT typedef CTVALUE_T FOO; // FOO is dependent on CT }; template <typename CT, typename CTBAR = typename CT::BAR> struct B { //typedef typename CT::BAR BAR; typedef CTBAR BAR; BAR foo() {return 0;} }; template <typename DT> struct VALUE_T__from__DT { typedef DT VALUE_T; }; template <typename DT, template <class T> class _C_ > struct BAR__from__DT { typedef typename A<_C_<DT> , typename VALUE_T__from__DT<DT>::VALUE_T >::FOO BAR; }; template <typename DT> struct C : B<C<DT>, typename BAR__from__DT<DT, C >::BAR > { //typedef DT VALUE_T; //typedef typename A<C>::FOO BAR; }; int main () { C<int> c; int x = c.foo(); (void)x; return 0; } 

Helper template type names are terrible, maybe you can find better names.

The main trick is that the definition of the problem typedef is placed in a separate meta container (i.e. a container for signs), so you can still do any voodoo there.

I commented on unnecessary typedefs (but left them there so you have a better chance of figuring out what I'm doing there).

Does this fit your need?

+4
source

Source: https://habr.com/ru/post/903200/


All Articles