11

If a template class definition contains a static member variable that depends on the template type, I'm unsure of what the reliable behavior should be?

In my case it is desirable to place the definition of that static member in the same .h file as the class definition, since

  1. I want the class to be general for many template data types that I don't currently know.
  2. I want only one instance of the static member to be shared throughout my program for each given template type. ( one for all MyClass<int> and one for all MyClass<double>, etc.

I can be most brief by saying that the code listed at this link behaves exactly as I want when compiled with gcc 4.3. Is this behavior according to the C++ Standard so that I can rely on it when using other compilers?

That link is not my code, but a counter example posted by CodeMedic to the discussion here. I've found several other debates like this one but nothing I consider conclusive.

I think the linker is consolidating the multiple definitions found ( in the example a.o and b.o ). Is this the required/reliable linker behavior?

Community
  • 1
  • 1
NoahR
  • 1,417
  • 3
  • 18
  • 33

3 Answers3

20

From N3290, 14.6:

A [...] static data member of a class template shall be defined in every translation unit in which it is implicitly instantiated [...], unless the corresponding specialization is explicitly instantiated [...] .

Typically, you put the static member definition in the header file, along with the template class definition:

template <typename T>
class Foo
{
  static int n;                       // declaration
};

template <typename T> int Foo<T>::n;  // definition

To expand on the concession: If you plan on using explicit instantiations in your code, like:

template <> int Foo<int>::n = 12;

then you must not put the templated definition in the header if Foo<int> is also used in other TUs other than the one containing the explicit instantiation, since you'd then get multiple definitions.

However, if you do need to set an initial value for all possible parameters without using explicit instantiation, you have to put that in the header, e.g. with TMP:

// in the header
template <typename T> int Foo<T>::n = GetInitialValue<T>::value;  // definition + initialization
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
  • Ok, so both the example code and behavior I am seeing are in compliance with the Standard then? Actually... here's what's missing to seal the win: What says that there won't be multiple instances of the specific static member in the program so the static variable would have the same value among all translation units? This as far as I can figure would need to be a require some consolidation at link time. – NoahR Aug 18 '11 at 14:54
  • @NoahR: all the instances of the static member will be linked to the same actual variable, so the value is truly shared. I think if you don't write an explicit initialization, you get default-initialization (or value-initialization, I forget; integers default to zero), but you might as well write `= 0;` in the header file. – Kerrek SB Aug 18 '11 at 14:56
  • 1
    Thanks @Kerrek SB. So static members of a template class get different treatment than static members of a non-template class. I say this because I have found plenty of examples to conclude that providing the definition of the static member in the .h file for the non-template class results in a link-time Duplicate Symbol Error and is a violation of the One Definition Rule. Anyway, the question at hand: Is link consolidation of a template class static member required by the Standard? – NoahR Aug 18 '11 at 15:15
  • @NoahR: I'd say the above quote from the standard implies that, though I haven't seen a more explicit statement to that effect. If you have duplicate definitions, check that you're not instantiating anything explicitly, I guess. – Kerrek SB Aug 18 '11 at 15:22
  • I see your point about the implied requirement. Thanks so much! The duplicate definitions reference was hypothetical for a different (non-template) case, but the source of my confusion in this case. – NoahR Aug 18 '11 at 15:30
  • Right. For non-template static members or globals, you typically provide the (unique) definition in a dedicated source file (for classes, usually the class definition file). I guess every global constant should come split as a header and a defining source file... yeah, it's easy to get confused. As a rule, never have definitions in the header; when you think of templates as *code generation tools* rather than actual code, than this present case doesn't violate that rule. – Kerrek SB Aug 18 '11 at 15:35
2

This is wholly an addition to @Kerrek SB's excellent answer. I'd add it as a comment, but there're many of them already, so the new comments are hidden by default.

So, his and other examples I saw are "easy" in the sense that type of static member variable is known beforehand. It's easy because compiler for example knows storage size for any template instantiation, so one may think that compiler could use funky mangling scheme, output variable definition once, and offload the rest to linker, and that might even work.

But it's a bit amazing that that it works when static member type depends on template parameter. For example, following works:

template <typename width = uint32_t>
class Ticks : public ITimer< width, Ticks<width> >
{
protected:
    volatile static width ticks;
}
template <typename width> volatile width Ticks<width>::ticks;

(Note that explicit instantiation of static var doesn't need (or allows) default spec for "width").

So, it brings more thoughts, that C++ compiler has to do quite a lot of processing - in particular, to instantiate a template, not only a template itself is needed, but it must also collect all [static member] explicit instantiations (one may only wonder then why they were made separate syntactic constructs, not something to be spelled out within the template class).

As for implementation of this on linker level, for GNU binutils its "common symbols": http://sourceware.org/binutils/docs/as/Comm.html#Comm . (For Microsoft toolchains, it's named COMDAT, as another answer says).

pfalcon
  • 6,724
  • 4
  • 35
  • 43
1

The linker handles such cases almost exactly the same as for non-template class static members with __declspec(selectany) declaration applied, like this:

class X {
public:
X(int i){};
};
__declspec(selectany) X x(1);//works in msvc, for gcc use __attribute__((weak))

And as msdn says: "At link time, if multiple definitions of a COMDAT are seen, the linker picks one and discards the rest... For dynamically initialized, global objects, selectany will discard an unreferenced object's initialization code, as well."

tav
  • 11
  • 2