为什么不允许非静态成员的地址作为模板非类型参数

Why is address of non-static member not allowed as template non-type parameter?

本文关键字:类型参数 地址 不允许 静态成员 为什么      更新时间:2023-10-16
template <int* ip> struct test {};
struct q {
    static int a;
    int b;
    
    constexpr q(int b_) : b(b_) {}
};
int i;
constexpr q q0(2);
int main()
{
    constexpr test<&i> t1;      // Works fine
    constexpr test<&q::a> t2;   // Works 
    constexpr test<&q0.b> t3;   // Does not work; address of non-static member?
    
    return 0;
}

尽管在编译时已知模板参数&q0.b但上述代码段中的 t3 声明失败。一些谷歌搜索显示,这是标准所不允许的(第14.3.2节(:

[注意:数组元素的地址和非静态类成员的名称或地址是不可接受的模板参数。

X<&amp;s.m> x4;//error: non-static membe 的地址

那么,为什么标准明确禁止这样做,尽管全局变量的非静态成员的地址是唯一的,并且在编译时是已知的?

首先,要使用指向子对象的指针/引用,您需要能够破坏它们。这是一项相当大的任务。

其次,可能更重要的是,来自 N4198:

常量表达式必须命名完整的限制 保留对象以避免指针出现锯齿问题 子对象:

struct A { int x, y; } a;
template<int*> struct Z;
using B = Z<&a.x + 1>;
using C = Z<&a.y>;
// Are B and C the same type?

引用理查德·史密斯的话,

答案"是"是有问题的,因为有些事情你可以做 带有指向 [ a.y ] 的指针,如果 在经过 [ a.x] 末尾的指针上执行,答案"否"是 有问题,因为它们(在典型实现中(表示 相同的地址。

这段代码替换你的 main

int main(void)
{    
constexpr static int bb = 5;    
constexpr test<&bb> t;    
return 0;
}

并且您将收到错误bb不能用作模板参数,因为它没有链接(不要与链接器相关人员混淆(。

类数据成员除非

通过对象引用,否则无法访问它们,并且在模板实例化期间不能考虑它们,因为数据成员没有链接,即它们不是定义的符号,因此不能用作模板参数。

话虽如此,做一个读数,你可以验证这一点:

48: 00000000004006ac     4 OBJECT  LOCAL  DEFAULT   14 q0

68: 000000000060097c     0 NOTYPE  GLOBAL DEFAULT  ABS __bss_start
69: 0000000000600978     4 OBJECT  GLOBAL DEFAULT   23 q::a

但是没有定义q0.b.另一种方法是命名(mangle(non静态类成员,这将剥夺语言的动态功能。