15
#include <map>
#include <iostream>
template <typename T>
class A 
{
 static std::map<int, int> data;
public:
 A()
 {
  std::cout << data.size() << std::endl;
  data[3] = 4;
 }
};

template <typename T>
std::map<int, int> A<T>::data;

//std::map<int, int> A<char>::data;

A<char> a;

int main()
{
 return 0;
}

What is wrong with this? Without explicit instantiation it breaks at

 data[3] = 4; 
Explicit instantiation solves the problem but the program breaks after
std::cout << data.size() << std::endl;
what means that the static class template memeber data was instantiated.
mrs
  • 151
  • 1
  • 3
  • 1
    What compiler? I don't think this is your fault. – Potatoswatter Sep 13 '10 at 22:47
  • This compiles fine using VS2010. – linuxuser27 Sep 13 '10 at 22:53
  • I am using vs2008 and it indeed compiles but the program breaks at line data[3] = 4 – mrs Sep 14 '10 at 11:18
  • 1
    @mrs: Ah, I didn't look closely enough at your code. You can't access `data[3]` until the `vector` has been resized to at least size `4`. `data.push_back(4)` on the other hand increments the size of `data` and initializes the new element. So, if it works properly in any case after changing to `data.push_back()`, then it's not a problem with the compiler after all. – Potatoswatter Sep 14 '10 at 14:14
  • 1
    @Potatoswatter The container is a map, not a vector; resize isn't required. – Jack Lloyd Sep 14 '10 at 16:49
  • @Jack: ah, I was nearly asleep when I said that and erased my answer. See the comments under my answer; OP says replacing `[]` with `push_back` fixes problem, but `map` doesn't even have `push_back`. So, it's a bit suspicious. Either an extremely primitive noncompliant implementation with "bonus" methods in `map`, or there's something OP isn't showing us. – Potatoswatter Sep 14 '10 at 19:28

3 Answers3

4

There is no explicit instantiation in your code.

There is no order of initialization of instantiated static data members among other static data members. So your code has effectively undefined behavior: Depending on whether the compiler first initializes the map or a, the reference to the map is valid or not.

See C++ Static member initialization.

Community
  • 1
  • 1
Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
  • I was trying std::vector instead of map and everything worked fine without explicit instantiation - you think that it is just luck? – mrs Sep 17 '10 at 11:02
  • and that is funny that in this example the first line in constructor std::cout << data.size() << std::endl; works as expected; the program breaks when I try to insert something into the map – mrs Sep 17 '10 at 11:06
  • That "explicit instantiation" is not an explicit instantiation. It's the definition of a static data member called `data` of an explicit specialization of the template `A` for `T = char`. There is no such explicit specialization. The compiler has to emit an error message for that code (if you comment it in). – Johannes Schaub - litb Sep 17 '10 at 11:25
2

I don't have Visual C++ handy, but I can see the same problem with your code compiling with GCC. You need the initialize the data member:

template<> std::map<int, int> A<char>::data = std::map<int, int>();

With this change, it compiles and runs correctly (for me on GCC on Linux).

Jack Lloyd
  • 8,215
  • 2
  • 37
  • 47
  • Is this needed because a separate instance of data is needed for each template initialized with a different type, in this char – Sirish Sep 14 '10 at 17:13
1

There're several errors in that code. First the initial idea is not good. You have two global static objects: a and A::data. The order in which they're initialized is undefined. Depending on the mood of your compiler you have a 50% chance to have constructor of a called first and attempted to write something into non-initialized A::data.

This is sometimes called static init order fiasco problem. A suggested solution is to turn such objects into local static objects by moving them into functions:

#include <map>
#include <iostream>

template <typename T>
class A
{
  std::map<int, int> &data()
  {
    static std::map<int, int> d;
    return d;
  }
public:
  A()
  {
    std::cout << data().size() << std::endl;
    data()[3] = 4;
  }
};

int main()
{
  A<char> a;
  return 0;
}

Local static objects are initialized on first call to the function.

About the commented out "explicit instantiation" you forgot template <>.

But after you prefix that line with template <> it's still not definition but a declaration. It declares that there's A::data definition somewhere else. To actually define it you need to initialize it with something, see Jack Lloyd answer for example.

Guest
  • 11
  • 1