这是我的工作代码示例:
#include <iostream>
template<typename B>
class b {
public:
int y;
constexpr b(int x) : y(x) {
}
constexpr void sayhi() {
std::cout << "hi" << std::endl;
}
};
template<int x>
struct A {
static constexpr b<int> bee = x;
static constexpr int y = x; // this one is fine and usable already, I don't have to do something like what I did on member bee
inline static void sayhi() {
std::cout << y << std::endl;
}
};
template<int x>
constexpr b<int> A<x>::bee; // why do I have to do something like this, it will cause errors if I don't.
int main(int argc, char** argv) {
A<30>::bee.sayhi(); // works fine
A<30>::sayhi(); // works fine
return 0;
}
我的代码做的很简单,我有模板结构A
,它有两个静态变量,即static constexpr int y
和static constexpr b<int> bee = x;
。我的模板struct A
将获取将由x
从模板参数复制的参数的值。我的问题是:为什么在课程方面,我必须通过这样的方式来初始化课程:
template<int x>
constexpr b<int> A<x>::bee;
如果我不使用上面的代码,我会收到undefined
引用错误。其中int已经很好了,可以通过做类似的事情来访问:
static constexpr int y = x;
我担心为什么我不能再向前宣布它了。
答案 0 :(得分:9)
static constexpr
成员在class { }
范围内初始化时具有值,但在class { }
之外定义之前,它在内存(地址)中没有位置。原因是您可能决定在链接库中包含部分或全部特殊化(例如.o
或.so
),或者是否默认为特殊化提供有效的内联链接。
如果使用对象的地址,则需要类外定义,这意味着它必须作为全局变量存在。另一方面,如果您希望constexpr
成员仅在编译时存在,禁止全局存储分配,则省略该定义是一个不错的选择。
顺便说一下,不允许将constexpr
说明符放在永远不能作为常量表达式计算的函数上,例如打印到sayhi
的{{1}}。这是一个“无需诊断(NDR)”规则,这意味着编译器现在可能不会抱怨,但下一个编译器版本可能会抱怨。
答案 1 :(得分:2)
由于 C++ 17 引入了静态内联变量并使静态 constexpr 变量隐式内联,您的问题的答案现在要简单得多。因此,在您的示例中,您可以简单地使用以下内容,而无需在类外声明任何变量 bee
(尽管出于兼容性原因仍然允许这样做):
template<int x>
struct A {
static constexpr b<int> bee = x;
};
来源:cppreference
<块引用>静态数据成员可以声明为内联。内联静态数据 成员可以在类定义中定义,并且可以指定一个 初始化程序。它不需要类外定义:
struct X
{
inline static int n = 1;
};
<块引用>
如果静态数据成员声明为 constexpr,则它是隐式内联的 并且不需要在命名空间范围内重新声明。这 没有初始化程序的重新声明(以前需要 [...]) 仍然允许使用,但已弃用。