Class 模板静态数据成员 definition/declaration/initialization

Class template static data-member definition/declaration/initialization

我知道这个问题已经被问过好几次了,我一直在阅读这样的帖子:

Initializing static members of a templated class

static member initialization for specialized template class

但是,我仍在努力将有关模板、专业化、静态数据成员定义和声明的所有部分组合在一起。

我有的是这样的:


template<size_t dim>
struct A {
  static std::array<float,dim> a1;
};

template<> 
std::array<float,1U> A<1U>::a1{1.};

template<> 
std::array<float,2U> A<2U>::a1{0.3,0.3};

int main() {
  std::array<float, 1U> v1 = A<1U>::a1;
  std::cout << v1[0] << std::endl;
  std::array<float, 2U> v2 = A<2U>::a1;
  std::cout << v2[0] << " " << v2[1] << std::endl;
  return 0;
}

此代码可在 GCC 9.2.0 和 MSVC2015 上编译。现在,我的理解是,如果多次包含这样的内容,可能会导致同一静态变量的多个定义,因为我们对模板进行了完全专门化。所以方法是将其移动到 cpp 文件,但在 hpp 中保留专业化声明。我会通过为模板实现添加一个 hpp 文件来使它更复杂一些:

//foo.hpp
template<size_t dim>
struct A {
  static std::array<float, dim> a1;
};
#include "fooImpl.hpp"

//fooImpl.hpp
template<>
std::array<float, 1U> A<1U>::a1;
template<>
std::array<float, 2U> A<2U>::a1;

//foo.cpp
#include"foo.hpp"

template<>
std::array<float, 1U> A<1U>::a1{ 1. };

template<>
std::array<float, 2U> A<2U>::a1{ 0.3,0.3 };

//main.cpp
int main() {
  std::array<float, 1U> v1 = A<1U>::a1;
  std::cout << v1[0] << std::endl;
  std::array<float, 2U> v2 = A<2U>::a1;
  std::cout << v2[0] << " " << v2[1] << std::endl;
  return 0;
}

此代码在 GCC9.2.0 上编译良好但在 MSVC2015 上失败,因为 a1 的重新定义。

正确的做法是什么?为什么 MSVC 抱怨?有没有办法让它对所有 c++11 兼容的编译器都是正确的和可移植的?

更新: 第一个代码在 MSVC 上没有提供正确的结果,它只显示零。为了使其正常工作,我需要从静态成员的初始化中删除 "template<>" 。但这会导致 GCC 中的代码无法编译。

更新 2: 我在这里找到了基本相同的问题并进行了更完整的分析:

然而没有人回答这个问题。

如果您对整个 class 进行专门化处理,则可以省略 cpp 中对 template<> 的使用。

下面的代码似乎可以解决您的目标,它在 MSVC (x86 V19.14)、gcc (x86-64 9.2) 和 clang (x86-64 9.0.0) 上编译 as tested on Compiler Explorer

template<size_t dim>
struct A {
  static std::array<float,dim> a1;
};

template<> 
struct A<1U> {
  static std::array<float,1U> a1;
};

template<> 
struct A<2U> {
  static std::array<float,2U> a1;
};

// cpp
std::array<float,1U> A<1U>::a1 {1.f};

std::array<float,2U> A<2U>::a1 {0.3f,0.3f};

int main() {
  std::array<float, 1U> v1 = A<1U>::a1;
  std::cout << v1[0] << std::endl;
  std::array<float, 2U> v2 = A<2U>::a1;
  std::cout << v2[0] << " " << v2[1] << std::endl;
  return 0;
}

为什么cpp中的定义不需要template<>?

根据 17.7.3 [temp.expl.spec] 第 5 段 (N4659),

[...] Members of an explicitly specialized class template are defined in the same manner as members of normal classes, and not using the template<> syntax. The same is true when defining a member of an explicitly specialized member class. [...]

注意这并不是说问题中的代码是错误的,而是因为 MSVC 对此不满意(并且可能是错误的......?)解决方法可能是上面提出的代码。

你击中了 a bug in MSVC。它显然已在 Visual Studio 2019 版本 16.5 预览版 2.

中修复

作为替代解决方法,您可以将定义保留在 header 中并将它们标记为内联 (since c++17):

template<> 
inline std::array<float,1U> A<1U>::a1{1.};

template<> 
inline std::array<float,2U> A<2U>::a1{0.3,0.3};