类模板静态数据成员的定义/声明/初始化

时间:2019-10-27 20:21:36

标签: c++ c++11 template-specialization static-members

我知道这个问题已经问过几次了,我一直在读类似的文章:

Initializing static members of a templated class

How can I Declare/define/initialize a static member variable of template classes as static member variables of a 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: 通过更完整的分析,我在这里找到了基本相同的问题:

Resolving Definitions of Specialized Static Member Variables of Templated Classes

但是没人回答这个问题。

2 个答案:

答案 0 :(得分:1)

如果您专门研究整个课程,则可以在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中的定义不需要模板<>?

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

  

[...]在下面定义显式专门的类模板的成员   与普通班级成员的方式相同,但不使用   template <>语法。当定义一个成员时也是如此   专门的成员类。 [...]

注意,这并不是说问题中的代码是错误的,但是由于MSVC不满意(并且可能是错误的...?),因此解决方法可能是代码以上建议。

答案 1 :(得分:1)

您打了a bug in MSVC。显然,它已在 Visual Studio 2019版本16.5预览版2 中修复。

作为替代解决方法,您可以将定义保留在标题中并将其标记为内联(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};