【问题标题】:Initializing static constexpr variables and classes inside a struct在结构中初始化静态 constexpr 变量和类
【发布时间】:2015-10-10 10:45:00
【问题描述】:

这是我的工作代码示例:

#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 ystatic constexpr b&lt;int&gt; bee = x;。我的模板结构A 将获取参数的值,该参数将由x 从模板参数中复制。我的问题是:当涉及到类时,我必须通过执行以下操作来初始化类:

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

如果我不使用上面的代码,我会收到 undefined 引用错误。其中 int 已经很好并且只需执行以下操作即可访问:

static constexpr int y = x;    

我担心为什么我不必再转发声明了。

【问题讨论】:

    标签: c++ struct constexpr


    【解决方案1】:

    由于 C++ 17 引入了静态内联变量并使静态 constexpr 变量隐式内联,因此您的问题的答案现在要简单得多。因此,在您的示例中,您可以简单地使用以下内容,而无需在类外声明任何变量 bee(尽管出于兼容性原因仍允许使用):

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

    来源:cppreference

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

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

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

    【讨论】:

      【解决方案2】:

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

      如果曾经使用过对象的地址,则需要类外定义,这意味着它必须作为全局变量存在。另一方面,如果您希望constexpr 成员仅在编译时存在,禁止全局存储分配,那么省略定义是一个不错的选择。

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

      【讨论】:

      • 啊啊啊啊,即使定义了它们,一开始也没有分配好?
      • @CarloBrew 根据官方标准术语,从技术上讲,它是“使用初始化程序声明的”,但没有“定义”为对象。类外声明就是定义。
      • 啊,我明白了,谢谢!评估为 constexpr 是什么意思?我应该如何避免这种情况?
      • 我没有说评估为 constexpr,我说评估为常量表达式……并避免将 constexpr 放在只能在运行时执行的函数上。 constexpr 说明符表明该函数在编译时是安全的,至少对于某些参数值而言。
      • 噢,因为cout 只会在运行时评估?
      猜你喜欢
      • 2021-08-07
      • 2012-08-18
      • 2011-01-07
      • 2011-06-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-05-13
      • 2011-08-22
      相关资源
      最近更新 更多