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};
我知道这个问题已经被问过好几次了,我一直在阅读这样的帖子:
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};