记C++中静态常量数据成员在不同标准下的一些区别

Note: 在C++中,class类型的静态常量数据成员分为两种:static const的和static constexpr的。

Note: 不同编译器对C++标准的支持程度不一样,在某个编译器上执行没问题不代表它一定符合了C++标准。


若整型或枚举类型的静态数据成员被声明为 const(且非 volatile ),则它能以身为常量表达式的类内初始值,直接在类定义内初始化,这不是必须的,也可以在类外初始化

struct X
{
    const static int n = 1;
    static const int m{2}; 	// C++11 起
    const static int k;		// 在类外初始化
};
const int X::k = 3;

C++11起,若将字面类型 (LiteralType) 的静态数据成员声明为 constexpr,则它必须以每个表达式均为常量表达式的类内初始值,直接在类定义内初始化

struct X {
    constexpr static int arr[] = { 1, 2, 3 };        // OK
    constexpr static std::complex<double> n = {1,2}; // OK
    constexpr static int k; // 错误:constexpr 静态数据成员要求类内初始化
};

ODR式 使用

对象被 ODR 式使用: 读取(除非它是编译时常量)或写入对象的值,取对象的地址,或将引用绑定到它;
引用被 ODR 式使用: 引用有被使用,且其所引用者在编译时未知;
函数被 ODR 式使用: 调用函数或取其地址。

C++11前
const 静态数据成员被 ODR 式使用,则仍需要在 命名空间作用域(即全局作用域)处 定义,但它不能在全局作用域中重复初始化。

struct X {
    static const int n = 1;		// 声明
};
const int *p = &X::n; 	// X::n 被 ODR 式使用
const int X::n;       	// 定义并使用类内初始值初始化

C++11起,C++17前
const 静态数据成员或 constexpr 静态数据成员被 ODR 式使用,则仍需要在命名空间作用域(即全局作用域)处定义,但它不能重复初始化。

struct X {
    static const int n = 1;		// 声明
    static constexpr int m = 4;	// 声明
};
const int *p = &X::n, *q = &X::m; 	// X::n 与 X::m 被 ODR 式使用
const int X::n;       				// 定义并使用类内初始值初始化
constexpr int X::m;         		// 在C++11~C++17中,这是一个定义

C++17起
const (且非 inline )静态数据成员被 ODR 式使用,则仍需要在命名空间作用域(即全局作用域)处定义,但它不能重复初始化。
constexpr 静态数据成员无论有没有被 ODR 式使用,都不需要在全局作用域处再次定义。当然可以在全局作用域处提供“定义”,尽管这是冗余的
若静态数据成员被声明为 constexpr ,则它隐式 inline 且不需要在命名空间作用域进行重声明。虽然这种无初始化值的重声明(之前则为必要,如上所示)仍然得到容许,但已被弃用。

struct X {
    static const int n = 1;		// 声明
    static constexpr int m = 4;
};
const int *p = &X::n, *q = &X::m; 	// X::n 与 X::m 被 ODR 式使用
const int X::n;       				// 一直都必须要定义
constexpr int X::m;         		// C++17 中的 X::m 不需要,可以注释掉

为了解答C++17起,关于`constexpr` 静态数据成员在全局作用域定义,为什么是冗余的?我们通过以下C++代码和汇编代码进行比较。

Note: 下面使用了支持C++17标准的GCC编译器、支持C++11标准的GCC编译器进行对比。因为gcc对C++标准的支持更全面。

例子一:

struct S {
	static constexpr int x = 42;
};
constexpr int S::x;

在这里插入图片描述
在这里插入图片描述

例子二:

struct S {
	static constexpr int x = 42;
};
//constexpr int S::x;

在这里插入图片描述
在这里插入图片描述

例子三:

struct S {
	static constexpr int x = 42;
};
//constexpr int S::x;
const int &AA = S::x;	// X::m 被 odr 式使用

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

在例子二中:
编译器看到X::m没有被 odr-used ,也没有显式定义,就做了一个小小的优化,不给这个常量固定分配空间了。但是它一旦被 odr-used ,编译器仍然会分配空间,就像例子三一样。
分配空间也会标为 weak_definition,像这样的多个定义最终会只取一个。

在例子三中:
可以看出在C++17标准下的constexpr 静态数据成员哪怕没有在全局作用域再次定义,编译器也会给其分配内存空间并初始化。相反的是在C++11标准下的constexpr 静态数据成员如果没有在全局作用域再次定义,编译器就不会给其分配内存空间并初始化,并在它被 odr-used 时报错:undefined reference to S::x

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值