Home > other >  Unable to initialize a template variable inside a class, why?
Unable to initialize a template variable inside a class, why?

Time:07-26

I don't understand why if I initialize this template variable globally, like this:

template <class T> struct null_string { static const std::string value; };
template<class T> const std::string null_string<T>::value = "";
template<class T> std::string const null_str = null_string<const T&>::value;

it works, but if I try to initialize it inside a class, like this:

class foo
 {
  template <class T> struct null_string { static const std::string value; };
  template<class T> const std::string null_string<T>::value = "";
  template<class T> std::string const null_str = null_string<const T&>::value;
 };

it gave me an error:

prove.cpp:8:65: error: invalid use of qualified-name ‘foo::null_string<T>::value’
    8 |     template<class T> const std::string null_string<T>::value = "";
      |                                                                 ^~
prove.cpp:11:78: error: data member ‘null_str’ cannot be a member template
   11 |   template<class T> std::string const null_str = null_string<const T&>::value;
      |  

Do you know why?

CodePudding user response:

Add missing static. Then, either move variable definitions to namespace scope:

class foo
{
    template <class T> struct null_string { static const std::string value; };
    template <class T> static std::string const null_str;
};

template <class T> const std::string foo::null_string<T>::value = "";
template <class T> std::string const foo::null_str = foo::null_string<const T&>::value;

Or make them inline:

class foo
{
    template <class T> struct null_string { inline static const std::string value = ""; };
    template <class T> inline static const std::string null_str = foo::null_string<const T&>::value;
};

CodePudding user response:

The problem is that you're providing the definition for the static data member in class-scope instead of namespace scope when the definition for a static data member must be placed in a namespace scope enclosing the member’s class definition. This can be seen from class.static.data#3 which states:

The declaration of a non-inline static data member in its class definition is not a definition and may be of an incomplete type other than cv void. The definition for a static data member that is not defined inline in the class definition shall appear in a namespace scope enclosing the member's class definition. In the definition at namespace scope, the name of the static data member shall be qualified by its class name using the ​::​ operator.

(emphasis mine)

Thus the correct way to do what you want would be as shown below:

class foo
 {
  template <class T> struct null_string 
  { 
      static const std::string value; //declaration not a definition
  };
//------------------vvvvvv---------------------------->added static here
  template<class T> static std::string const null_str;//declaration not a definition
 };

 //provide definition for static members at global scope
template<class T> const std::string foo::null_string<T>::value = "";
template<class T> std::string const foo::null_str = foo::null_string<const T&>::value;

Demo


With C 17, you can also use inline to provide in-class definition for the static data members.

class foo
 {
  template <class T> struct null_string 
  { 
//----vvvvvv--------------------------------->  inline used here
      inline static const std::string value = "";    //definition
  };
//--------------------------vvvvvv--------------------------->added static here
  template<class T> inline static std::string const null_str = foo::null_string<const T&>::value;//definition
//------------------^^^^^^----------------------------------->inline used here
 };
  • Related