2

I was working with a template class that contains a static variable. The structure of code is as follows.

Header.h

template<class T> class Foo
{
public:
    static int count;
    Foo() {
        count++;
    }
    void printCount() {
        cout << count << endl;
    }
};
template<class T> int Foo<T>::count;

Source.cpp

#include "Header.h"
template<> int Foo<int>::count = 5;

main.cpp

#include <iostream>
using namespace std;
#include "Header.h"
int main()
{
    Foo<int> obj1;
    Foo<int> obj2;
    obj1.printCount();
    obj2.printCount();
    return 0;
}

the output on xcode8.3.3 is:

 7
 7

whereas the output on Visual Studio 2015 is:

2
2

i.e. the specific instantiation overrides the generic instantiation in xcode8.3.3 but not in Visual Studio 2015. Could someone explain this difference in behavior? Thanks in advance.

paskol
  • 33
  • 7

3 Answers3

2

While your code contains a constraint violation, it is in fact not too difficult to make it well formed and maintain the one place where the static is initialized. For the C++ standard says at [temp.expl.spec]/13:

An explicit specialization of a static data member of a template or an explicit specialization of a static data member 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 ]

The above implies that merely adding this line

template<> int Foo<int>::count; // declaration

At the bottom of Header.h is enough to let all translation units know that the Foo<int>::count exists "somewhere". The one true definition may remain in Source.cpp.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
1

Your program is not valid:

[temp.expl.spec#6] If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required.

in other words, translation units should agree on what a template name means.

The next standard paragraph laconically goes on with:

The placement of explicit specialization declarations for [... basically, any template whatsoever ...], can affect whether a program is well-formed according to the relative positioning of the explicit specialization declarations and their points of instantiation in the translation unit as specified above and below. When writing a specialization, be careful about its location; or to make it compile will be such a trial as to kindle its self-immolation.

Massimiliano Janes
  • 5,524
  • 1
  • 10
  • 22
0

gcc complains about double instances:

/tmp/ccUEOg7s.o:(.bss._ZN3FooIiE5countE[_ZN3FooIiE5countE]+0x0): multiple definition of `Foo<int>::count'
/tmp/ccG7CO9C.o:(.data+0x0): first defined here

So having two instances and a linker which is not able to see this, any result can be the effect.

It is simply a failure to have two instances of your variable here, independent if it is a templated instance or not. You have to remove one of them.

If using C++17 you can have the var inline in the header.

Klaus
  • 24,205
  • 7
  • 58
  • 113