【问题标题】:Compile errors for simple CRTP case with dependent types具有依赖类型的简单 CRTP 案例的编译错误
【发布时间】:2013-08-22 13:41:02
【问题描述】:

我正在尝试使用一种简单形式的 CRTP(Curiously Recurring Template Pattern),因为我有几个类,每个类都有几个相关的类,我想要一种将它们绑定在一起的方法(例如,我有类例如 Widget、Doobry 和 Whatsit,以及相关的类 WidgetHandle、DoobryHandle 和 WhatsitHandle)。

我用来从Base 继承的每个类都添加了一个value_type 类型定义,以便基类可以将其称为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;
}

但是,我遇到了编译错误:

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'

这是 VS2010 的问题,尽管我在 clang 中遇到了类似的错误。我在这里错过了什么?

【问题讨论】:

  • 当您将 Widget 作为参数传递给 Base 时,它是不完整的类型。
  • 事实上,我在任何地方都没有看到value_type 的定义。
  • @PetrBudnik 在Widget body 的开头。
  • @jrok 我已经纠正了。
  • Here 是另一种可能的解决方法。

标签: c++ crtp


【解决方案1】:

修改Base的定义,将句柄类型作为第二个参数,避免循环依赖。

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

您还可以使用特征类来获取 Widget 的 WidgeHandle 类型。

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

【讨论】:

  • 我知道我可以在模板定义中添加额外的类型,但不幸的是,如果 TWrapper 有一种方法可以指定它绑定到的类型
  • 使用默认模板参数从特征类中获取句柄类型——那么至少客户端代码看起来是一样的。
  • 这样就行了。在我的例子中,每个类都有几个相关的类型,因此将它们与 Handle 特征类绑定在一起意味着它们可以全部声明在一个类中。
【解决方案2】:

您不能有循环依赖:Base 需要在 Base 实例化时未知的 Widget value_type。

可能的解决方案是:将 value_type 作为 Base 模板参数传递,使用额外的 Traits 模板,...

示例

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

另一个(略有不同)示例

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-01-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多