我知道这个问题已经问过几次了,我一直在读类似的文章:
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: 通过更完整的分析,我在这里找到了基本相同的问题:
Resolving Definitions of Specialized Static Member Variables of Templated Classes
但是没人回答这个问题。
答案 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};