初始化结构体中的静态 constexpr 变量和类

问题描述 投票:0回答:2

这是我的工作代码示例:

#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
从模板参数复制。我的问题是:为什么当涉及到类时,我必须通过执行以下操作来初始化类:

template<int x>
constexpr b<int> A<x>::bee; 

如果我不使用上面的代码,我会收到

undefined
参考错误。其中 int 已经很好并且可以通过执行以下操作来访问:

static constexpr int y = x;    

我担心为什么我不需要再转发申报了。

c++ struct constexpr
2个回答
16
投票

static constexpr
成员在
class { }
范围内初始化时具有值,但在内存中没有位置(地址),直到它在
class { }
之外定义为止。原因是您可能决定将其部分或全部专业化包含在链接库中(例如
.o
.so
),或者是否默认为专业化提供有效的内联链接。

如果曾经使用过对象的地址,则需要类外定义,这意味着它必须作为全局变量存在。另一方面,如果您希望

constexpr
成员仅在编译时存在,禁止全局存储分配,那么省略定义是一个不错的选择。

顺便说一下,不允许将

constexpr
说明符放在永远不能作为常量表达式求值的函数上,例如打印为
sayhi
std::cout
。这是“无需诊断 (NDR)”规则,意味着编译器现在可能不会抱怨,但下一个编译器版本可能会抱怨。


6
投票

由于 C++ 17 引入了静态内联变量并使静态 constexpr 变量隐式内联,因此您的问题的答案现在要简单得多。因此,在您的示例中,您可以简单地使用以下内容,而无需在类外部声明任何变量

bee
(尽管出于兼容性原因仍然允许):

template<int x>
struct A {
    static constexpr b<int> bee = x;
};

来源:cppreference

静态数据成员可以声明为内联。内联静态数据 成员可以在类定义中定义,并且可以指定一个 初始化程序。它不需要类外定义:

struct X
{
    inline static int n = 1;
};

如果静态数据成员声明为 constexpr,则它是隐式内联的 并且不需要在命名空间范围内重新声明。这 没有初始化程序的重新声明(以前需要[...]) 仍然允许,但已被弃用。

© www.soinside.com 2019 - 2024. All rights reserved.