2

I'm using CRT pattern and want the base class to see typedefs from the derived class. In this post @James McNellis suggested to do that using base_traits class and it works fine. But in the case described in that post the derived class itself is a template. This approach does not work in VS2010 when the derived class is not a template.

template <class D>
struct base_traits;
template <class D>
struct base
{
    typedef typename base_traits<D>::value_t value_t;
};

struct derived : base<derived>
{
    typedef typename base_traits<derived>::value_t value_t;
};

template<>
struct base_traits<derived>
{
    typedef int value_t;
};

The above code gives lots of errors. The first one is:

error C2027: use of undefined type 'base_traits

on the line of the base class's typedef.

Community
  • 1
  • 1
Mihran Hovsepyan
  • 10,810
  • 14
  • 61
  • 111

2 Answers2

6

base_traits<derived> must be declared and defined prior it's usage since it is needed for the implicit instancation of base<derived> (below, I forward declared derived) :

template <class D>
struct base_traits;


template <class D>
struct base
{
    typedef typename base_traits<D>::value_t value_t;
};

struct derived;

template<>
struct base_traits<derived>
{
    typedef int value_t;
};

struct derived : base<derived>
{
    typedef base_traits<derived>::value_t value_t;
};


int main(void)
{
    derived d;
}

Live demo

quantdev
  • 23,517
  • 5
  • 55
  • 88
  • Generally the definition of the base and declaration of the base_traits classes should go to one header and the definition of the derived and its base_traits specialization to another header, and the second header should include the first one, so having base_traits specialization for the derived class defined before the base class is practically not usable. – Mihran Hovsepyan Sep 04 '14 at 09:44
  • @MihranHovsepyan indeed, the above order is now equivalent to `base_traits` , `base` in one header, and the `base_traits<>` specialization + `derived` in another. Hope this helps. – quantdev Sep 04 '14 at 09:51
  • I see, the above answer is written in that way. Thank you. – Mihran Hovsepyan Sep 04 '14 at 10:19
4

§14.7.3 [temp.expl.spec]/p7:

The placement of explicit specialization declarations for function templates, class templates, variable templates, member functions of class templates, static data members of class templates, member classes of class templates, member enumerations of class templates, member class templates of class templates, member function templates of class templates, static data member templates of class templates, member functions of member templates of class templates, member functions of member templates of non-template classes, static data member templates of non-template classes, member function templates of member classes of class templates, etc., and the placement of partial specialization declarations of class templates, variable templates, member class templates of non-template classes, static data member templates of non-template classes, member class templates of class templates, etc., can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

In particular (§14.7.3 [temp.expl.spec]/p6),

If a template [...] is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and [...] the specialization is used in a way that would cause an implicit instantiation to take place [...], the program is ill-formed, no diagnostic required.

The explicit specialization base_traits<derived> must be declared and defined before the definition of derived, as otherwise both inheriting from base<derived> and using base_traits<derived>::value_t would trigger an implicit instantiation. Thus:

template <class D>
struct base_traits;

template <class D>
struct base
{
    typedef typename base_traits<D>::value_t value_t;
};

struct derived;

template<>
struct base_traits<derived>
{
    typedef int value_t;
};

struct derived : base<derived>
{
    typedef base_traits<derived>::value_t value_t;
};
T.C.
  • 133,968
  • 17
  • 288
  • 421