【问题标题】:C++ template static member instantiationC++模板静态成员实例化
【发布时间】:2011-04-11 22:09: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;
}

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

处中断
 数据[3] = 4; 
显式实例化解决了问题,但是程序在
std::cout  意味着静态类模板成员data被实例化了。
    

【问题讨论】:

  • 什么编译器?我不认为这是你的错。
  • 使用 VS2010 可以正常编译。
  • 我正在使用 vs2008,它确实可以编译,但程序在行 data[3] = 4 处中断
  • @mrs:啊,我没有仔细看你的代码。在vector 至少调整为4 大小之前,您无法访问data[3]。另一方面,data.push_back(4) 增加data 的大小并初始化新元素。所以,如果改成data.push_back()后无论如何都能正常工作,那毕竟不是编译器的问题。
  • @Potatoswatter 容器是地图,不是向量;不需要调整大小。

标签: c++ templates static instantiation member


【解决方案1】:

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

这有时被称为static init order fiasco 问题。一个建议的解决方案是通过将这些对象移动到函数中来将它们变成本地静态对象:

#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 &lt;&gt;的注释掉的“显式实例化”。

但是在你给那行加上template &lt;&gt; 前缀之后,它仍然不是定义,而是一个声明。它声明有 A::data 定义其他地方。要实际定义它,您需要用一些东西对其进行初始化,例如,请参阅 Jack Lloyd 的答案。

【讨论】:

    【解决方案2】:

    您的代码中没有明确的实例化。

    实例化的静态数据成员在其他静态数据成员中没有初始化顺序。因此,您的代码实际上具有未定义的行为:取决于编译器首先初始化映射还是 a,对映射的引用是否有效。

    C++ Static member initialization

    【讨论】:

    • 我尝试用 std::vector 代替 map 并且在没有显式实例化的情况下一切正常 - 你认为这只是运气?
    • 有趣的是,在这个例子中,构造函数 std::cout
    • 那个“显式实例化”不是显式实例化。它是一个名为data 的静态数据成员的定义,是T = char 的模板A 的显式特化。没有这种明确的专业化。编译器必须为该代码发出错误消息(如果您对其进行注释)。
    【解决方案3】:

    我没有方便的 Visual C++,但我可以看到使用 GCC 编译的代码存在同样的问题。您需要初始化数据成员:

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

    通过此更改,它可以正确编译和运行(对于我在 Linux 上的 GCC 上)。

    【讨论】:

    • 是否需要这样做,因为在这个字符中,每个用不同类型初始化的模板都需要一个单独的数据实例
    猜你喜欢
    • 2017-10-03
    • 1970-01-01
    • 1970-01-01
    • 2011-01-14
    • 1970-01-01
    • 2012-02-02
    • 1970-01-01
    • 1970-01-01
    • 2020-10-27
    相关资源
    最近更新 更多