Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Compile errors for simple CRTP case with dependent types

Tags:

c++

crtp

I'm trying to use a simple form of CRTP (Curiously Recurring Template Pattern) as I've got several classes, each with several related classes, and I want a means of binding them together (eg I've got classes like Widget, Doobry and Whatsit, with related classes WidgetHandle, DoobryHandle and WhatsitHandle).

Each class that I'm using to inherit from Base adds a value_type typedef so that the base class can refer to it as typename TWrapper::value_type.

struct WidgetHandle {};

template <typename TWrapper>
class Base
{
public:
    Base(typename TWrapper::value_type value_) 
        : value(value_) {}

    typename TWrapper::value_type   value;
};

class Widget : public Base<Widget>
{
public:
    typedef WidgetHandle value_type;

    Widget(WidgetHandle value_) : Base<Widget>(value_) {}
};

int main(int argc, char* argv[])
{

    Widget i(WidgetHandle());
    return 0;
}

However, I'm getting compilation errors:

scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget'
scratch1.cpp(16) : see declaration of 'Widget'
scratch1.cpp : see reference to class template instantiation 'Base<TWrapper>' being compiled
1>          with
1>          [
1>              TWrapper=Widget
1>          ]
scratch1.cpp(10): error C2039: 'value_type' : is not a member of 'Widget'

This is with VS2010, though I'm getting similar errors with clang. What am I missing here?

like image 507
the_mandrill Avatar asked Aug 22 '13 13:08

the_mandrill


1 Answers

Change the definition of Base to take the handle type as a second parameter to avoid the circular dependency.

struct WidgetHandle {};

template <typename TWrapper, typename HandleType>
class Base
{
public:
    typedef HandleType value_type;

    Base(HandleType value_) 
        : value(value_) {}

    HandleType value;
};

class Widget : public Base<Widget, WidgetHandle>
{
public:
    Widget(WidgetHandle value_) : Base(value_) {}
};

int main(int argc, char* argv[])
{

    Widget i(WidgetHandle());
    return 0;
}

You could also use a traits class to get the WidgeHandle type for Widget.

struct WidgetHandle {};
class Widget;

template<class T>
struct Handle
{
};

template<>
struct Handle<Widget>
{
  typedef WidgetHandle type;  
};

template <typename TWrapper, typename HandleType = Handle<TWrapper>::type>
class Base
{
public:
    typedef HandleType value_type;

    Base(HandleType value_) 
        : value(value_) {}

    HandleType value;
};

class Widget : public Base<Widget>
{
public:
    Widget(WidgetHandle value_) : Base(value_) {}
};

int main(int argc, char* argv[])
{

    Widget i(WidgetHandle());
    return 0;
}
like image 128
paxos1977 Avatar answered Nov 11 '22 05:11

paxos1977