5

I have a similar problem as this one:

"undefined reference" to static field template specialization

but the workaround they use won't work for me.

I have a CRTP class with static data members, one of which is a std::mutex. Unfortunately, GCC's (4.8.2) linker is giving me an "undefined reference" error for this mutex. Clang (3.4) does not. Is there a workaround? The original question (linked above) invoked the copy constructor on the static data member that forced GCC to emit a symbol, but since my data member is a std::mutex, that's not an option -- the copy constructor is deleted, and there are no argument constructors. Am I just hosed?

I don't believe the problem is with std::mutex, I think the problem is with how GCC handles static data members in template classes that rely on default constructors.

Thanks for any help!

Here's a slim version of my problem: test.hh

#include <mutex>

template < class T >
class CRTP_class {
public:
  T * ptr_;
  static std::mutex mutex_; // linker error here
  static int clearly_a_problem_with_mutex_; // no linker error here
};

class Foo : public CRTP_class< Foo >
{
public:
  void set_bar( int setting );
  int bar_;
};

test.cc

#include <test.hh>

template<> std::mutex CRTP_class< Foo >::mutex_;
template<> int CRTP_class< Foo >::clearly_a_problem_with_mutex_( 0 );

void Foo::set_bar( int setting ) {
  std::lock_guard< std::mutex > locker( mutex_ );
  ++clearly_a_problem_with_mutex_;
  bar_ = setting;
}

main.cc

#include <test.hh>

int main() {
  Foo foo;
  foo.set_bar( 5 );
}

And then I compile with this command:

g++ -std=c++0x main.cc test.cc -I.

to get the error

/tmp/cclyxUfC.o: In function `Foo::set_bar(int)':
test.cc:(.text+0x86): undefined reference to `CRTP_class<Foo>::mutex_'
collect2: error: ld returned 1 exit status

(Edit 1: In response to the commenter who suggested that this is a duplicate bug with "why do templates have to be in header files" -- There is nothing odd about putting the separate template specializations into the .cc files instead of into the .hh files -- which is really what you need in the case where you've got a mutex and you want, for obvious reasons, only a single copy of that mutex. If you declare a static data member in a header file, then each translation unit that #includes the header will end up with their own copy of the mutex in which case it's not serving its job of ensuring mutual exclusion)

(Edit 2: Ooops! I linked to the wrong prior bug.)

Community
  • 1
  • 1
Andrew
  • 199
  • 1
  • 9
  • Don't put your template implementation into a separate translation unit, but in the header where you're going to declare it. – πάντα ῥεῖ Nov 14 '14 at 17:47
  • See also [this](http://stackoverflow.com/questions/25671850/error-lnk2005-quest-treeenter-oneclass-quest-treequest-node-class-std/25672020#25672020) and [this](http://stackoverflow.com/questions/25139535/multiple-definition-when-using-mock-header-files-for-templates/25139592#25139592) Q&A. – πάντα ῥεῖ Nov 14 '14 at 17:53
  • In this case, it is perfectly reasonable to put the static data members in the .cc file -- that is not the problem (see the clearly_a_problem_with_mutex_ variable), so I'd appreciate if you could unmark this issue as a duplicate so I can post an answer. I got an answer from GCC that I'll post here. In particular, changing template<> std::mutex CRTP_class< Foo >::mutex_; into template<> std::mutex CRTP_class< Foo >::mutex_{}; fixes this test case. – Andrew Nov 14 '14 at 18:50
  • OK, I'm going to remove the duplicate mark for [Why can templates only be implemented in the header file?](http://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file). Expecting your answer. – πάντα ῥεῖ Nov 14 '14 at 18:53
  • CRTP certainly makes a difference, sorry I didn't spot this while flying through your 1st code sample. – πάντα ῥεῖ Nov 14 '14 at 18:59

1 Answers1

4

From GCC's Jonathan Wakely on bugzilla:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=63876

The problem seems to be that I didn't provide an intializer for the mutex. The syntax for that is to provide an open and close curly braces

template<> std::mutex CRTP_class< Foo >::mutex_;

becomes

template<> std::mutex CRTP_class< Foo >::mutex_{};

(There is no issue with having the mutex live in the .cc file)

Andrew
  • 199
  • 1
  • 9
  • 1
    That is a quite sophisticated distinction between declaration and definition (which I don't like). +1 –  Nov 14 '14 at 19:02