【问题标题】:Class template static data-member definition/declaration/initialization类模板静态数据成员定义/声明/初始化
【发布时间】:2019-10-27 20:21:36
【问题描述】:

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

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.xml 中保留专业化声明。我将通过为模板实现添加一个 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

但是没有人回答这个问题。

【问题讨论】:

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


    【解决方案1】:

    如果您专门研究整个课程,则可以在 cpp 中省略 template&lt;&gt;

    下面的代码似乎可以解决您的目标,它可以在 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),

    [...] 显式特化类模板的成员定义在 与普通类的成员相同的方式,并且不使用 模板 语法。定义一个成员时也是如此 明确专门化的成员类。 [...]

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

    【讨论】:

    • 感谢您的工作回答。对我来说似乎很奇怪,必须专门化结构才能准确地说明模板的用途。你还说“专用版本初始化中的静态成员没有模板化,所以你确实不应该在cpp中使用模板”但是如果我把它们拿走,那么gcc和clang就会抱怨
    • 所以这个答案实际上是错误的? stackoverflow.com/a/2342696/8066678
    • 确实如此,感谢您的回答。我觉得我仍然缺少一些东西
    • 我认为您在问题中提供的代码是正确的,而 MSVC 将声明视为重新定义是错误的。解决方法可能是我提出的解决方案。
    【解决方案2】:

    你点击了a bug in MSVC。它显然已在 Visual Studio 2019 版本 16.5 Preview 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};
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-03-10
      • 1970-01-01
      • 1970-01-01
      • 2021-10-21
      • 2019-05-03
      • 2011-03-14
      相关资源
      最近更新 更多