CRTP特征仅适用于模板化派生类

时间:2019-03-20 10:27:13

标签: c++11 c++14

我已经看到在CRTP模式的基类中使用派生类型特征的惯用法,如下所示:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

template <typename T>
struct Derived1 : Base<Derived1<T>>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};

template <typename T>
struct traits<Derived1<T>> {
    using size_type = size_t;
};

int main()
{
    using T = float;
    Derived1<T> d1;
    d1.print();
}

我的理解是,该习惯用法的目的是延迟Base类的size_type的实例化。我感到困惑的是,只有在派生类本身是模板化的情况下,这种模式才似乎有效。例如,如果我们将代码更改为:

template<typename Derived>
struct traits;

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

struct Derived1 : Base<Derived1>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};

template <>
struct traits<Derived1> {
    using size_type = size_t;
};

int main()
{
    Derived1 d1;
    d1.print();
}

然后我们得到错误

prog.cc: In instantiation of 'struct Base<Derived1>':
prog.cc:21:19:   required from here
prog.cc:18:58: error: invalid use of incomplete type 'struct traits<Derived1>'
     using size_type = typename traits<Derived>::size_type;
                                                          ^
prog.cc:14:8: note: declaration of 'struct traits<Derived1>'
 struct traits;
        ^~~~~~
prog.cc: In function 'int main()':
prog.cc:33:9: error: 'Derived1' is not a template
         Derived1<float> d1;

有人可以给我一个解释,说明为什么模板化的派生类可以编译,而未模板化的类不能编译吗?

2 个答案:

答案 0 :(得分:1)

您看到的编译错误与CRTP无关,只是一点点依赖关系。

在没有模板的代码中,您的“ Base”结构需要特殊的“ traits”结构的定义,但仅在之后出现,因此它尝试使用在上面的声明中看到的不完整类型。

要使代码正常工作,您需要在Base声明之前具有“ traits”专业名称,这要求您还添加Derived 1声明,这是编译代码:

class Derived1;

template<typename Derived>
struct traits;

template <>
struct traits<Derived1> {
    using size_type = size_t;
};

template<typename Derived>
struct Base {
    using size_type = typename traits<Derived>::size_type;
};

struct Derived1 : Base<Derived1>{
    using size_type = size_t;
    void print(){ std::cout << "Derived1" << std::endl; }
};


int main()
{
    Derived1 d1;
    d1.print();
}

答案 1 :(得分:1)

您看到的问题与CRTP无关。

这是标准所提到的。

  

如果在实例化点(13.7.4.1)已声明但未定义类模板,   实例化产生不完整的类类型(6.7)。 [示例:

template<class T> class X; X<char> ch; // error: incomplete type
X<char>

您的traits仅在实例化Base<Derived>时才声明,因此根据标准(请参见上文从标准中提取的内容),{{1} }产生不完整的类型。

您应该重新排列代码的顺序,以便在实例化struct traits<Derived>时看到traits<Derived>的特殊化。