문제

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?

도움이 되었습니까?

해결책

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;
}

다른 팁

You can not have circular dependencies: Base needs the Widget value_type which is unknown at Base instantiation.

Possible Solution would be: Pass the value_type as a Base template parameter, use an extra Traits template, ...

Example:

template <typename W> 
struct WidgetTraits {};

template <typename W>
class Base
{
    public:
    typedef typename WidgetTraits<W>::value_type value_type;
};

class Widget;

template<>
struct WidgetTraits<Widget>
{
    typedef WidgetHandle value_type;
};

class Widget : public Base<Widget>
{
};

Another (slightly different) Example:

template <typename C, typename A>
class B : public A
{
    public:
    typedef typename A::value_type value_type;
};


class A
{
    public:
    typedef WidgetHandle value_type;
};


class C : public B<C, A>
{
};
라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top