Why does this code give the error, “template speci

2019-04-30 01:16发布

问题:

When I try to compile this with 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() { }

I get

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

I don't understand the error at all. What is wrong with my definition of _field and how do I fix it?

(Note that the arguments to _field are not necessarily the same for all subclasses.)

回答1:

For the compiler to identify this as a template specialization (e.g. to be able to check the syntax), you need the template keyword:

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

Its brackets are empty as all template parameters are specialized, but you cannot just leave it away.



回答2:

The error says exactly what is missing. template<> is missing before that line.

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

Note, however, that your typing of Field<Class>, if unique, could be used to construct all instances of Field<Class> with a given string.

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() { }

which means that every instance of Field<Class> always has the name "blah".

One question I would have is, do you really need storage for said Field<Class> to actually have a pointer to a string, and if so does it need to be unique, and if so does it need to be "bare"? Because figuring out where the static instance exists is somewhat annoying.

Together with field_traits above:

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

this moves the problem of "where is the _field stored" to being the compilers problem. And it is initialized by the contents of field_traits<T>::get_name().



回答3:

A static data member must have both a declaration and a definition. If this was a plain class it would look like this:

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

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

Templates aren't ordinarily defined in source files, so the code would look something like this:

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

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

In your code, you don't have the definition of the static data member. That's okay if you don't use it. But the code that the compiler is complaining about defines a static data member for CRTP<Class>; that's a specialization (because it's not applicable to all instantiations of CRTP, just to this one), and the compiler is saying that you have to tell it that it's a specialization. So do as you're told:

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

or, to write the non-specialized template version, use the usual template syntax:

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