在结构中初始化静态 constexpr 变量和 类
Initializing static constexpr variables and classes inside a struct
这是我的工作代码示例:
#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;
。我的模板结构 A
将获取参数的值,该值将由 x
从模板参数中复制。我的问题是:当涉及到 classes 时,为什么我必须通过执行以下操作来初始化 class:
template<int x>
constexpr b<int> A<x>::bee;
如果我不使用上面的代码,我会收到 undefined
引用错误。其中 int 已经很好并且可以通过执行以下操作访问:
static constexpr int y = x;
我很担心为什么我不必再转发声明它了。
static constexpr
成员在 class { }
范围内初始化时有一个值,但在 [=11= 范围外定义之前它在内存中没有位置(地址) ].原因是您可能决定将其部分或全部专业化包含在 link 库中(例如 .o
或 .so
),或者是否提供有效内联 link默认为专业化年龄。
如果曾经使用过对象的地址,则需要超出 class 的定义,这意味着它必须作为全局变量存在。另一方面,如果你希望 constexpr
成员只在编译时存在,禁止全局存储分配,那么省略定义是一个不错的选择。
顺便说一句,不允许将 constexpr
说明符放在永远不能作为常量表达式求值的函数上,例如 sayhi
打印为 std::cout
。这是一个 "no diagnostic required (NDR)" 规则,意味着编译器现在可能不会抱怨,但下一个编译器版本可能会抱怨。
由于 C++ 17 引入了静态内联变量并使静态 constexpr 变量隐式内联,因此您的问题的答案现在变得更加简单。因此,在您的示例中,您可以简单地使用以下内容而无需在 class 之外声明任何变量 bee
(尽管出于兼容性原因仍然允许这样做):
template<int x>
struct A {
static constexpr b<int> bee = x;
};
来源:cppreference
A static data member may be declared inline. An inline static data
member can be defined in the class definition and may specify an
initializer. It does not need an out-of-class definition:
struct X
{
inline static int n = 1;
};
If a static data member is declared constexpr, it is implicitly inline
and does not need to be redeclared at namespace scope. This
redeclaration without an initializer (formerly required [...])
is still permitted, but is deprecated.
这是我的工作代码示例:
#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;
。我的模板结构 A
将获取参数的值,该值将由 x
从模板参数中复制。我的问题是:当涉及到 classes 时,为什么我必须通过执行以下操作来初始化 class:
template<int x>
constexpr b<int> A<x>::bee;
如果我不使用上面的代码,我会收到 undefined
引用错误。其中 int 已经很好并且可以通过执行以下操作访问:
static constexpr int y = x;
我很担心为什么我不必再转发声明它了。
static constexpr
成员在 class { }
范围内初始化时有一个值,但在 [=11= 范围外定义之前它在内存中没有位置(地址) ].原因是您可能决定将其部分或全部专业化包含在 link 库中(例如 .o
或 .so
),或者是否提供有效内联 link默认为专业化年龄。
如果曾经使用过对象的地址,则需要超出 class 的定义,这意味着它必须作为全局变量存在。另一方面,如果你希望 constexpr
成员只在编译时存在,禁止全局存储分配,那么省略定义是一个不错的选择。
顺便说一句,不允许将 constexpr
说明符放在永远不能作为常量表达式求值的函数上,例如 sayhi
打印为 std::cout
。这是一个 "no diagnostic required (NDR)" 规则,意味着编译器现在可能不会抱怨,但下一个编译器版本可能会抱怨。
由于 C++ 17 引入了静态内联变量并使静态 constexpr 变量隐式内联,因此您的问题的答案现在变得更加简单。因此,在您的示例中,您可以简单地使用以下内容而无需在 class 之外声明任何变量 bee
(尽管出于兼容性原因仍然允许这样做):
template<int x>
struct A {
static constexpr b<int> bee = x;
};
来源:cppreference
A static data member may be declared inline. An inline static data member can be defined in the class definition and may specify an initializer. It does not need an out-of-class definition:
struct X { inline static int n = 1; };
If a static data member is declared constexpr, it is implicitly inline and does not need to be redeclared at namespace scope. This redeclaration without an initializer (formerly required [...]) is still permitted, but is deprecated.