C++模板静态成员实例化

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;
}

这里有什么问题?如果没有显式实例化,在

 data[3] = 4; 
处会出错。显式实例化解决了这个问题,但在
std::cout << data.size() << std::endl;
后程序会出错,这意味着静态类模板成员 data 已被实例化。


1
什么编译器?我不认为这是你的错。 - Potatoswatter
这在使用VS2010编译时可以正常通过。 - linuxuser27
我正在使用vs2008,它确实编译了,但程序在data[3] = 4行中断。 - mrs
1
@mrs: 啊,我没有仔细看你的代码。在将vector大小调整至至少为4之前,您无法访问data[3]。 另一方面,data.push_back(4)会增加data的大小并初始化新元素。因此,如果更改为data.push_back()后在任何情况下都正常工作,则不是编译器的问题。 - Potatoswatter
1
@Potatoswatter 容器是一个映射,而不是一个向量;不需要调整大小。 - Jack Lloyd
@Jack:啊,我差点就睡着了,然后删掉了我的回答。看一下我回答下面的评论;OP说用push_back替换[]可以解决问题,但是map甚至没有push_back。所以这有点可疑。要么是一个极其原始不兼容的实现,带有map中的“奖励”方法,要么是OP没有向我们展示某些东西。 - Potatoswatter
3个回答

4

你的代码中没有显式实例化。

在其他静态数据成员中,没有初始化已实例化的静态数据成员的顺序。因此,你的代码具有未定义行为:取决于编译器是先初始化map还是a,map的引用是有效的还是无效的。

请参见C++静态成员初始化


我尝试使用std::vector代替map,而且一切都正常工作,没有显式实例化 - 你认为这只是运气吗? - mrs
这很有趣,在这个例子中构造函数中的第一行 std::cout << data.size() << std::endl; 的效果如预期一样;但当我尝试将某些内容插入到 map 中时,程序会崩溃。 - mrs
“显式实例化”并不是显式实例化。它是模板“A”针对“T = char”的显式特化的静态数据成员data的定义。但是,没有这样的显式特化。编译器必须为该代码发出错误消息(如果您将其注释)。 - Johannes Schaub - litb

2

我手头没有Visual C++,但是我发现你的代码在使用GCC编译时存在同样的问题。你需要初始化数据成员:

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

通过这个变化,它可以在GCC on Linux上编译和正确运行(对我来说)。


这是否需要是因为每个用不同类型初始化的模板都需要一个单独的数据实例,比如 char。 - Sirish

1

这段代码中有几个错误。首先,初始想法不好。你有两个全局静态对象:aA::data。它们初始化的顺序是未定义的。根据编译器的心情,你有50%的机会首先调用a的构造函数并尝试写入未初始化的A::data

这有时被称为静态初始化顺序混乱问题。建议的解决方案是通过将它们移入函数中将这些对象变成本地静态对象:

#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;
}

本地静态对象在第一次调用函数时初始化。

关于被注释的“显式实例化”,您忘记了template <>

但是,在您使用 template <> 前缀该行之后,它仍然不是定义,而是一个声明。它声明了 A::data 定义在其他地方。要实际定义它,您需要使用某些东西进行初始化,请参见 Jack Lloyd 的答案。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接