Question

Yes, I know, there is a question with almost the same title, but it refers to a different situation (giving the same clang error message). In my case, I have a .cpp file with a big anonymous namespace (containing implementation details). In that namespace is a traits class template with a static data member, which I need to access from outside the anonymous namespace. Let me flesh this out a bit:

file.hpp

namespace bar {
  template<typename A>
  struct foo
  {
    static_assert(is_same<A,float>::value || is_same<A,double>::value, "");
    static void set_static_var(A const&x);
    // ...
  };
}

and

file.cpp

namespace {
  template<typename A>
  struct foo_traits
  {
    // lots of static code supporting the implementation of bar::foo<>
    static A datum;
  };
  template<> float  foo_traits<float>::datum;   // no change if this is in global namespace
  template<> double foo_traits<double>::datum;
  template struct foo_traits<float>;
  template struct foo_traits<double>;
}

namespace bar {
  template<typename A>
  void foo<A>::set_static_var(A const&x)
  {
    foo_traits<A>::datum = x;
  }
  template struct foo<double>;     // error only appears if these lines are present.
  template struct foo<float>;      // but without these lines, the whole file is void.
}

and I get the said error on the variables foo_traits<>::datum (and later linkage failure). Note added in edit I use clang++ -std=c++11 -stdlib=libc++ (version 3.3), which produced only a warning, but (as I've said) in my real application that warning is backed up by a linkage failure (exactly the said symbols are missing). No compiler warnings with gcc, though. Perhaps this is a clang bug? end note

What is the correct way to define those variables? Note that declaring them outside the anonymouns namespace won't compile. Note also that, AFAIK, the same construct with an ordinary non-template just works.

Note I'm not asking on how to circumvent this problem (that I can think of myself), but how this is correctly done.

Was it helpful?

Solution

I think the correct solution is simply:

namespace {
  template<typename A>
  struct foo_traits
  {
    // lots of static code supporting the implementation of bar::foo<>
    static A datum;
  };
  template<typename A> A foo_traits<A>::datum;
}

The problem with your code, AFAICT, is that

float foo_traits<float>::datum;

refers to a specialization for foo_traits which does not exist. (the template<> in your code is also not allowed, Clang gives an error for it as well).

See this live example.

OTHER TIPS

The problem is that a declaration of an explicit specialization of a static data member is a definition only if it includes an initializer, and a declaration otherwise. C++11 14.7.3/13:

An explicit specialization of a static data member of a template is a definition if the declaration includes an initializer; otherwise, it is a declaration. [ Note: The definition of a static data member of a template that requires default initialization must use a braced-init-list:

template<> X Q<int>::x; // declaration
template<> X Q<int>::x (); // error: declares a function
template<> X Q<int>::x { }; // definition

—end note ]

Clang correctly compiles program if you provide initializers for the specializations:

template<> float  foo_traits<float>::datum = 0;
template<> double foo_traits<double>::datum = 0;

I was erroneous in my belief that the original program was correct, apparently gcc incorrectly treats the declarations as definitions even when they lack initializers.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top