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

Class template static data-member definition/declaration/initialization

本文关键字:声明 初始化 定义 数据成员 静态      更新时间:2023-10-16

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

初始化模板类的静态成员

如何将模板类的静态成员变量声明/定义/初始化为类的静态会员变量?

专用模板类的静态成员初始化

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

我有一些类似的东西:


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:我在这里通过更完整的分析发现了基本相同的问题:

解析模板类的专用静态成员变量的定义

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

如果专门化整个类,则可以省略cpp中template<>的使用。

下面的代码似乎解决了您的目标,它在MSVC(x86 V19.14(、gcc(x86-64 9.2(和clang(x86-64.9.0.0(上编译,在编译器资源管理器上测试:

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[温度解释规范]第5段(N4659(,

[…]显式专用类模板的成员在中定义与普通类的成员相同的方式,并且不使用模板<>语法。定义显式专门化的成员类。[…]

注意这并不是说问题中的代码是错误的,但由于MSVC对此不满意(可能是错误的…?(,解决方法可能是上面提出的代码。

您在MSVC中遇到了一个错误。它显然已在Visual Studio 2019 16.5预览版2中修复。

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