为什么此代码会出错,“模板专业化需要'模板<>'”?

时间:2013-03-30 21:46:46

标签: c++ templates clang template-specialization crtp

当我尝试用Clang编译它时

template<class T>
struct Field
{
    char const *name;
    Field(char const *name) : name(name) { }
};

template<class Derived>
class CRTP { static Field<Derived> const _field; };

class Class : public CRTP<Class> { };
Field<Class>   const CRTP<Class>::_field("blah");

int main() { }

我得到了

error: template specialization requires 'template<>'
Field<Class>   const CRTP<Class>::_field("blah");
                     ~~~~~~~~~~~  ^

我根本不明白这个错误。我对_field的定义有什么问题?如何解决?

(注意_field的参数对于所有子类不一定相同。)

3 个答案:

答案 0 :(得分:12)

为了让编译器将其识别为模板特化(例如,为了能够检查语法),您需要template关键字:

template<>
Field<Class> const CRTP<Class>::_field("blah");

它的括号是空的,因为所有模板参数都是专用的,但你不能把它丢弃。

答案 1 :(得分:2)

错误说明究竟缺少什么。该行之前缺少template<>

template<>
Field<Class> const CRTP<Class>::_field("blah");

但请注意,您输入Field<Class>(如果唯一)可用于构造具有给定字符串的Field<Class>的所有实例。

template<typename T>
struct field_trait;

template<class T>
struct Field
{
    char const *name;
    Field() : name(field_trait<T>::get_name()) {}
};

template<class Derived>
class CRTP { static Field<Derived> const _field; };
template<class Derived>
class CRTP<Derived>::_field;

class Class;

template<>
struct field_traits<Class> {
  static const char* get_name() { return "blah"; }
};

class Class : public CRTP<Class> { };

int main() { }

表示Field<Class>的每个实例始终具有名称"blah"

我要问的一个问题是,你真的需要存储器来说Field<Class>实际上有一个指向字符串的指针,如果是这样,它需要是唯一的,如果需要,它需要是“裸”?因为找出static实例存在的位置有点烦人。

与上面的field_traits一起:

template<class Derived>
class CRTP { static Field<Derived>& field() const { static Field<Derived> _field( field_traits<Derived>::get_name()); return _field; };

这将“存储_field的位置”问题转变为编译器问题。它由field_traits<T>::get_name()

的内容初始化

答案 2 :(得分:2)

静态数据成员必须同时具有声明和定义。如果这是一个简单的类,它将如下所示:

// header:
class C {
    static int i;
};

// source:
int C::i = 3;

模板通常不在源文件中定义,因此代码看起来像这样:

// header:
template <class T>
class C {
    static int i;
};

template <class T>
int C<T>::i = 3;

在您的代码中,您没有静态数据成员的定义。如果你不使用它,那没关系。但是编译器抱怨的代码定义了CRTP<Class>的静态数据成员;这是一个专门化(因为它不适用于CRTP的所有实例化,只适用于这一个),并且编译器说你必须告诉它它是一个特化。就像你被告知的那样:

template <>
Field<Class> const CRTP<Class>::_field("blah");

或者,要编写非专业模板版本,请使用通常的模板语法:

template <class T>
Field<T> const CRTP<T>::_field("blah");